Refactor: Clarify code around scheduling composite nodes
[blender.git] / source / blender / python / intern / bpy_rna_context.cc
blobe5ce525ac3bd33b44808058920d9235e30caedef
1 /* SPDX-FileCopyrightText: 2023 Blender Authors
3 * SPDX-License-Identifier: GPL-2.0-or-later */
5 /** \file
6 * \ingroup pythonintern
8 * This file adds some helper methods to the context, that cannot fit well in RNA itself.
9 */
11 #include <Python.h>
13 #include "BLI_listbase.h"
14 #include "BLI_utildefines.h"
16 #include "BKE_context.hh"
17 #include "BKE_main.hh"
18 #include "BKE_screen.hh"
19 #include "BKE_workspace.hh"
21 #include "WM_api.hh"
22 #include "WM_types.hh"
24 #include "bpy_rna_context.hh"
26 #include "../generic/python_compat.hh"
28 #include "RNA_access.hh"
29 #include "RNA_prototypes.hh"
31 #include "bpy_rna.hh"
33 /* -------------------------------------------------------------------- */
34 /** \name Private Utility Functions
35 * \{ */
37 static void bpy_rna_context_temp_set_screen_for_window(bContext *C, wmWindow *win, bScreen *screen)
39 if (screen == nullptr) {
40 return;
42 if (screen == WM_window_get_active_screen(win)) {
43 return;
46 WorkSpace *workspace;
47 BKE_workspace_layout_find_global(CTX_data_main(C), screen, &workspace);
48 /* Changing workspace instead of just screen as they are tied. */
49 WM_window_set_active_workspace(C, win, workspace);
50 WM_window_set_active_screen(win, workspace, screen);
53 /**
54 * Switching to or away from this screen is not supported.
56 static bool wm_check_screen_switch_supported(const bScreen *screen)
58 if (screen->temp != 0) {
59 return false;
61 if (BKE_screen_is_fullscreen_area(screen)) {
62 return false;
64 return true;
67 static bool wm_check_window_exists(const Main *bmain, const wmWindow *win)
69 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
70 if (BLI_findindex(&wm->windows, win) != -1) {
71 return true;
74 return false;
77 static bool wm_check_screen_exists(const Main *bmain, const bScreen *screen)
79 if (BLI_findindex(&bmain->screens, screen) != -1) {
80 return true;
82 return false;
85 static bool wm_check_area_exists(const wmWindow *win, const bScreen *screen, const ScrArea *area)
87 if (win && (BLI_findindex(&win->global_areas.areabase, area) != -1)) {
88 return true;
90 if (screen && (BLI_findindex(&screen->areabase, area) != -1)) {
91 return true;
93 return false;
96 static bool wm_check_region_exists(const bScreen *screen,
97 const ScrArea *area,
98 const ARegion *region)
100 if (screen && (BLI_findindex(&screen->regionbase, region) != -1)) {
101 return true;
103 if (area && (BLI_findindex(&area->regionbase, region) != -1)) {
104 return true;
106 return false;
109 /** \} */
111 /* -------------------------------------------------------------------- */
112 /** \name Temporary Context Override (Python Context Manager)
113 * \{ */
115 struct ContextStore {
116 wmWindow *win;
117 bool win_is_set;
118 bScreen *screen;
119 bool screen_is_set;
120 ScrArea *area;
121 bool area_is_set;
122 ARegion *region;
123 bool region_is_set;
126 struct BPyContextTempOverride {
127 PyObject_HEAD /* Required Python macro. */
128 bContext *context;
130 ContextStore ctx_init;
131 ContextStore ctx_temp;
133 struct {
135 * The original screen of `ctx_temp.win`, needed when restoring this windows screen as it
136 * won't be `ctx_init.screen` (when switching the window as well as the screen), see #115937.
138 bScreen *screen;
139 } ctx_temp_orig;
141 /** Bypass Python overrides set when calling an operator from Python. */
142 bContext_PyState py_state;
144 * This dictionary is used to store members that don't have special handling,
145 * see: #bpy_context_temp_override_extract_known_args,
146 * these will then be accessed via #BPY_context_member_get.
148 * This also supports nested *stacking*, so a nested temp-context-overrides
149 * will overlay the new members on the old members (instead of ignoring them).
151 PyObject *py_state_context_dict;
154 static void bpy_rna_context_temp_override_dealloc(BPyContextTempOverride *self)
156 PyObject_GC_UnTrack(self);
157 Py_CLEAR(self->py_state_context_dict);
158 PyObject_GC_Del(self);
161 static int bpy_rna_context_temp_override_traverse(BPyContextTempOverride *self,
162 visitproc visit,
163 void *arg)
165 Py_VISIT(self->py_state_context_dict);
166 return 0;
169 static int bpy_rna_context_temp_override_clear(BPyContextTempOverride *self)
171 Py_CLEAR(self->py_state_context_dict);
172 return 0;
175 static bool bpy_rna_context_temp_override_enter_ok_or_error(const BPyContextTempOverride *self,
176 const Main *bmain,
177 const wmWindow *win,
178 const bScreen *screen,
179 const ScrArea *area,
180 const ARegion *region)
183 /* NOTE(@ideasman42): Regarding sanity checks.
184 * There are 3 different situations to be accounted for here regarding overriding windowing data.
186 * - 1) Nothing is overridden.
187 * Simple, no sanity checks needed.
189 * - 2) Some members are overridden.
190 * Check the state is consistent (that the region is part the area or screen for e.g.).
192 * - 3) Some members are overridden *but* the context members are unchanged.
193 * This is a less obvious case which often happens when a Python script copies the context
194 * typically via `context.copy()`, manipulates it and passes it in as keyword arguments.
196 * A naive approach could be to behave as if these arguments weren't passed in
197 * which would work in many situations however there is a difference
198 * since these members are used to restore the context afterwards.
199 * It's possible a script might use this context-manager to *pin* the context,
200 * running actions that change the context, relying on the context to be restored.
202 * When error-checking unchanged context members some error checks must be skipped
203 * such as the check to disallow temporary screens since that could break using
204 * `temp_override(..)` running with the current context from a render-window for e.g.
206 * In fact all sanity checks could be disabled when the members involved remain unchanged
207 * however it's possible Python scripts corrupt Blender's internal windowing state so keeping
208 * the checks is harmless and alerts developers early on that something is wrong.
211 if (self->ctx_temp.region_is_set && (region != nullptr)) {
212 if (screen == nullptr && area == nullptr) {
213 PyErr_SetString(PyExc_TypeError, "Region set with screen & area set to None");
214 return false;
216 if (!wm_check_region_exists(screen, area, region)) {
217 PyErr_SetString(PyExc_TypeError, "Region not found in area or screen");
218 return false;
222 if (self->ctx_temp.area_is_set && (area != nullptr)) {
223 if (win == nullptr && screen == nullptr) {
224 PyErr_SetString(PyExc_TypeError, "Area set with window & screen set to None");
225 return false;
227 if (!wm_check_area_exists(win, screen, area)) {
228 PyErr_SetString(PyExc_TypeError, "Area not found in screen");
229 return false;
233 if (self->ctx_temp.screen_is_set && (screen != nullptr)) {
234 if (win == nullptr) {
235 PyErr_SetString(PyExc_TypeError, "Screen set with null window");
236 return false;
238 if (!wm_check_screen_exists(bmain, screen)) {
239 PyErr_SetString(PyExc_TypeError, "Screen not found");
240 return false;
243 /* Skip some checks when the screen is unchanged. */
244 if (self->ctx_init.screen_is_set) {
245 /* Switching away from a temporary screen isn't supported. */
246 if ((self->ctx_init.screen != nullptr) &&
247 !wm_check_screen_switch_supported(self->ctx_init.screen))
249 PyErr_SetString(PyExc_TypeError,
250 "Overriding context with an active temporary screen isn't supported");
251 return false;
253 if (!wm_check_screen_switch_supported(screen)) {
254 PyErr_SetString(PyExc_TypeError,
255 "Overriding context with temporary screen isn't supported");
256 return false;
258 if (BKE_workspace_layout_find_global(bmain, screen, nullptr) == nullptr) {
259 PyErr_SetString(PyExc_TypeError, "Screen has no workspace");
260 return false;
263 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
264 LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
265 if (win_iter == win) {
266 continue;
268 if (screen == WM_window_get_active_screen(win_iter)) {
269 PyErr_SetString(PyExc_TypeError, "Screen is used by another window");
270 return false;
277 if (self->ctx_temp.win_is_set && (win != nullptr)) {
278 if (!wm_check_window_exists(bmain, win)) {
279 PyErr_SetString(PyExc_TypeError, "Window not found");
280 return false;
284 return true;
287 static PyObject *bpy_rna_context_temp_override_enter(BPyContextTempOverride *self)
289 bContext *C = self->context;
290 Main *bmain = CTX_data_main(C);
292 /* It's crucial to call #CTX_py_state_pop if this function fails with an error. */
293 CTX_py_state_push(C, &self->py_state, self->py_state_context_dict);
295 self->ctx_init.win = CTX_wm_window(C);
296 self->ctx_init.screen = self->ctx_init.win ? WM_window_get_active_screen(self->ctx_init.win) :
297 CTX_wm_screen(C);
298 self->ctx_init.area = CTX_wm_area(C);
299 self->ctx_init.region = CTX_wm_region(C);
301 wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
302 bScreen *screen = self->ctx_temp.screen_is_set ? self->ctx_temp.screen : self->ctx_init.screen;
303 ScrArea *area = self->ctx_temp.area_is_set ? self->ctx_temp.area : self->ctx_init.area;
304 ARegion *region = self->ctx_temp.region_is_set ? self->ctx_temp.region : self->ctx_init.region;
306 self->ctx_init.win_is_set = (self->ctx_init.win != win);
307 self->ctx_init.screen_is_set = (self->ctx_init.screen != screen);
308 self->ctx_init.area_is_set = (self->ctx_init.area != area);
309 self->ctx_init.region_is_set = (self->ctx_init.region != region);
311 /* When the screen isn't passed but a window is, match the screen to the window,
312 * it's important to do this after setting `self->ctx_init.screen_is_set` because the screen is
313 * *not* set, only the window, restoring the window will also restore its screen, see #116297. */
314 if ((self->ctx_temp.win_is_set == true) && (self->ctx_temp.screen_is_set == false)) {
315 screen = win ? WM_window_get_active_screen(win) : nullptr;
318 if (!bpy_rna_context_temp_override_enter_ok_or_error(self, bmain, win, screen, area, region)) {
319 CTX_py_state_pop(C, &self->py_state);
320 return nullptr;
323 /* Manipulate the context (setup). */
324 if (self->ctx_temp.screen_is_set) {
325 self->ctx_temp_orig.screen = WM_window_get_active_screen(win);
326 bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp.screen);
329 /* NOTE: always set these members, even when they are equal to the current values because
330 * setting the window (for e.g.) clears the area & region, setting the area clears the region.
331 * While it would be useful in some cases to leave the context as-is when setting members
332 * to their current values.
334 * Favor predictable behavior, where setting a member *always* clears the nested
335 * values it contains - no matter the state of the current context.
336 * If this difference is important, the caller can always detect this case and avoid
337 * passing in the context override altogether. */
339 if (self->ctx_temp.win_is_set) {
340 CTX_wm_window_set(C, self->ctx_temp.win);
342 if (self->ctx_temp.screen_is_set) {
343 CTX_wm_screen_set(C, self->ctx_temp.screen);
345 if (self->ctx_temp.area_is_set) {
346 CTX_wm_area_set(C, self->ctx_temp.area);
348 if (self->ctx_temp.region_is_set) {
349 CTX_wm_region_set(C, self->ctx_temp.region);
352 Py_RETURN_NONE;
355 static PyObject *bpy_rna_context_temp_override_exit(BPyContextTempOverride *self,
356 PyObject * /*args*/)
358 bContext *C = self->context;
360 Main *bmain = CTX_data_main(C);
362 /* Manipulate the context (restore). */
363 if (self->ctx_temp.screen_is_set) {
364 if (self->ctx_temp_orig.screen && wm_check_screen_exists(bmain, self->ctx_temp_orig.screen)) {
365 wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
366 if (win && wm_check_window_exists(bmain, win)) {
367 /* Disallow switching away from temporary-screens & full-screen areas, while it could be
368 * useful to support this closing a these screens uses different and more involved logic
369 * compared with switching between user managed screens, see: #117188. */
370 if (wm_check_screen_switch_supported(WM_window_get_active_screen(win))) {
371 bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen);
377 /* Account for the window to be freed on file-read,
378 * in this case the window should not be restored, see: #92818.
379 * Also account for other windowing members to be removed on exit,
380 * in this case the context is cleared. */
381 bool do_restore = true;
383 /* Restore context members as needed.
385 * The checks here behaves as follows:
386 * - When `self->ctx_init.win_is_set` is true, the window was changed by the override.
387 * in this case restore the initial window.
388 * - When `self->ctx_temp.win_is_set` is true, the window was set to the current value.
389 * Setting the window (even to the current value) must be accounted for
390 * because setting the window clears the area and the region members,
391 * which must now be restored.
393 * `is_container_set` is used to detect if nested context members need to be restored.
394 * The comments above refer to the window, it also applies to the screen containing an area
395 * and area which contains a region. */
396 bool is_container_set = false;
398 /* Handle Window. */
399 if (do_restore) {
400 if (self->ctx_init.win && !wm_check_window_exists(bmain, self->ctx_init.win)) {
401 CTX_wm_window_set(C, nullptr);
402 do_restore = false;
405 if (do_restore) {
406 if (self->ctx_init.win_is_set) {
407 CTX_wm_window_set(C, self->ctx_init.win);
408 is_container_set = true;
410 else if (self->ctx_temp.win_is_set) {
411 if (self->ctx_init.win == CTX_wm_window(C)) {
412 is_container_set = true;
414 else {
415 /* If the context changed, it's incorrect to attempt to restored nested members,
416 * in this case leave the context as-is, see: #119202. */
417 do_restore = false;
423 /* Handle Screen. */
424 if (do_restore) {
425 if (self->ctx_init.screen && !wm_check_screen_exists(bmain, self->ctx_init.screen)) {
426 CTX_wm_screen_set(C, nullptr);
427 do_restore = false;
430 if (do_restore) {
431 if (self->ctx_init.screen_is_set || is_container_set) {
432 CTX_wm_screen_set(C, self->ctx_init.screen);
433 is_container_set = true;
435 else if (self->ctx_temp.screen_is_set) {
436 if (self->ctx_init.screen == CTX_wm_screen(C)) {
437 is_container_set = true;
439 else {
440 do_restore = false;
446 /* Handle Area. */
447 if (do_restore) {
448 if (self->ctx_init.area &&
449 !wm_check_area_exists(self->ctx_init.win, self->ctx_init.screen, self->ctx_init.area))
451 CTX_wm_area_set(C, nullptr);
452 do_restore = false;
455 if (do_restore) {
456 if (self->ctx_init.area_is_set || is_container_set) {
457 CTX_wm_area_set(C, self->ctx_init.area);
458 is_container_set = true;
460 else if (self->ctx_temp.area_is_set) {
461 if (self->ctx_init.area == CTX_wm_area(C)) {
462 is_container_set = true;
464 else {
465 do_restore = false;
471 /* Handle Region. */
472 if (do_restore) {
473 if (self->ctx_init.region &&
474 !wm_check_region_exists(self->ctx_init.screen, self->ctx_init.area, self->ctx_init.region))
476 CTX_wm_region_set(C, nullptr);
477 do_restore = false;
480 if (do_restore) {
481 if (self->ctx_init.region_is_set || is_container_set) {
482 CTX_wm_region_set(C, self->ctx_init.region);
483 is_container_set = true;
485 /* Enable is there is ever data nested within the region. */
486 else if (false && self->ctx_temp.region_is_set) {
487 if (self->ctx_init.region == CTX_wm_region(C)) {
488 is_container_set = true;
490 else {
491 do_restore = false;
496 UNUSED_VARS(is_container_set, do_restore);
498 /* Finished restoring the context. */
500 /* A copy may have been made when writing context members, see #BPY_context_dict_clear_members */
501 PyObject *context_dict_test = static_cast<PyObject *>(CTX_py_dict_get(C));
502 if (context_dict_test && (context_dict_test != self->py_state_context_dict)) {
503 Py_DECREF(context_dict_test);
505 CTX_py_state_pop(C, &self->py_state);
507 Py_RETURN_NONE;
510 #if (defined(__GNUC__) && !defined(__clang__))
511 # pragma GCC diagnostic push
512 # pragma GCC diagnostic ignored "-Wcast-function-type"
513 #endif
515 static PyMethodDef bpy_rna_context_temp_override_methods[] = {
516 {"__enter__", (PyCFunction)bpy_rna_context_temp_override_enter, METH_NOARGS},
517 {"__exit__", (PyCFunction)bpy_rna_context_temp_override_exit, METH_VARARGS},
518 {nullptr},
521 #if (defined(__GNUC__) && !defined(__clang__))
522 # pragma GCC diagnostic pop
523 #endif
525 static PyTypeObject BPyContextTempOverride_Type = {
526 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
527 /*tp_name*/ "ContextTempOverride",
528 /*tp_basicsize*/ sizeof(BPyContextTempOverride),
529 /*tp_itemsize*/ 0,
530 /*tp_dealloc*/ (destructor)bpy_rna_context_temp_override_dealloc,
531 /*tp_vectorcall_offset*/ 0,
532 /*tp_getattr*/ nullptr,
533 /*tp_setattr*/ nullptr,
534 /*tp_as_async*/ nullptr,
535 /*tp_repr*/ nullptr,
536 /*tp_as_number*/ nullptr,
537 /*tp_as_sequence*/ nullptr,
538 /*tp_as_mapping*/ nullptr,
539 /*tp_hash*/ nullptr,
540 /*tp_call*/ nullptr,
541 /*tp_str*/ nullptr,
542 /*tp_getattro*/ nullptr,
543 /*tp_setattro*/ nullptr,
544 /*tp_as_buffer*/ nullptr,
545 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
546 /*tp_doc*/ nullptr,
547 /*tp_traverse*/ (traverseproc)bpy_rna_context_temp_override_traverse,
548 /*tp_clear*/ (inquiry)bpy_rna_context_temp_override_clear,
549 /*tp_richcompare*/ nullptr,
550 /*tp_weaklistoffset*/ 0,
551 /*tp_iter*/ nullptr,
552 /*tp_iternext*/ nullptr,
553 /*tp_methods*/ bpy_rna_context_temp_override_methods,
554 /*tp_members*/ nullptr,
555 /*tp_getset*/ nullptr,
556 /*tp_base*/ nullptr,
557 /*tp_dict*/ nullptr,
558 /*tp_descr_get*/ nullptr,
559 /*tp_descr_set*/ nullptr,
560 /*tp_dictoffset*/ 0,
561 /*tp_init*/ nullptr,
562 /*tp_alloc*/ nullptr,
563 /*tp_new*/ nullptr,
564 /*tp_free*/ nullptr,
565 /*tp_is_gc*/ nullptr,
566 /*tp_bases*/ nullptr,
567 /*tp_mro*/ nullptr,
568 /*tp_cache*/ nullptr,
569 /*tp_subclasses*/ nullptr,
570 /*tp_weaklist*/ nullptr,
571 /*tp_del*/ nullptr,
572 /*tp_version_tag*/ 0,
573 /*tp_finalize*/ nullptr,
574 /*tp_vectorcall*/ nullptr,
577 /** \} */
579 /* -------------------------------------------------------------------- */
580 /** \name Context Temporary Override Method
581 * \{ */
583 static PyObject *bpy_context_temp_override_extract_known_args(const char *const *kwds_static,
584 PyObject *kwds)
586 PyObject *sentinel = Py_Ellipsis;
587 PyObject *kwds_parse = PyDict_New();
588 for (int i = 0; kwds_static[i]; i++) {
589 PyObject *key = PyUnicode_FromString(kwds_static[i]);
590 PyObject *val = _PyDict_Pop(kwds, key, sentinel);
591 if (val != sentinel) {
592 if (PyDict_SetItem(kwds_parse, key, val) == -1) {
593 BLI_assert_unreachable();
596 Py_DECREF(key);
597 Py_DECREF(val);
599 return kwds_parse;
602 /* NOTE(@ideasman42): `ContextTempOverride` isn't accessible from (without creating an instance),
603 * it should be exposed although it doesn't seem especially important either. */
604 PyDoc_STRVAR(
605 /* Wrap. */
606 bpy_context_temp_override_doc,
607 ".. method:: temp_override(*, window=None, area=None, region=None, **keywords)\n"
608 "\n"
609 " Context manager to temporarily override members in the context.\n"
610 "\n"
611 " :arg window: Window override or None.\n"
612 " :type window: :class:`bpy.types.Window`\n"
613 " :arg screen: Screen override or None.\n"
614 "\n"
615 " .. note:: Switching to or away from full-screen areas & temporary screens "
616 "isn't supported. Passing in these screens will raise an exception, "
617 "actions that leave the context such screens won't restore the prior screen.\n"
618 "\n"
619 " .. note:: Changing the screen has wider implications "
620 "than other arguments as it will also change the works-space "
621 "and potentially the scene (when pinned).\n"
622 "\n"
623 " :type screen: :class:`bpy.types.Screen`\n"
624 " :arg area: Area override or None.\n"
625 " :type area: :class:`bpy.types.Area`\n"
626 " :arg region: Region override or None.\n"
627 " :type region: :class:`bpy.types.Region`\n"
628 " :arg keywords: Additional keywords override context members.\n"
629 " :return: The context manager .\n"
630 " :rtype: ContextTempOverride\n");
631 static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds)
633 const PointerRNA *context_ptr = pyrna_struct_as_ptr(self, &RNA_Context);
634 if (context_ptr == nullptr) {
635 return nullptr;
638 if (kwds == nullptr) {
639 /* While this is effectively NOP, support having no keywords as it's more involved
640 * to return an alternative (dummy) context manager. */
642 else {
643 /* Needed because the keywords copied into `kwds_parse` could contain anything.
644 * As the types of keys aren't checked. */
645 if (!PyArg_ValidateKeywordArguments(kwds)) {
646 return nullptr;
650 struct {
651 BPy_StructRNA_Parse window;
652 BPy_StructRNA_Parse screen;
653 BPy_StructRNA_Parse area;
654 BPy_StructRNA_Parse region;
655 } params{};
656 params.window.type = &RNA_Window;
657 params.screen.type = &RNA_Screen;
658 params.area.type = &RNA_Area;
659 params.region.type = &RNA_Region;
661 static const char *const _keywords[] = {
662 "window",
663 "screen",
664 "area",
665 "region",
666 nullptr,
668 static _PyArg_Parser _parser = {
669 PY_ARG_PARSER_HEAD_COMPAT()
670 "|$" /* Optional, keyword only arguments. */
671 "O&" /* `window` */
672 "O&" /* `screen` */
673 "O&" /* `area` */
674 "O&" /* `region` */
675 ":temp_override",
676 _keywords,
677 nullptr,
679 /* Parse known keywords, the remaining keywords are set using #CTX_py_state_push. */
680 kwds = kwds ? PyDict_Copy(kwds) : PyDict_New();
682 PyObject *kwds_parse = bpy_context_temp_override_extract_known_args(_keywords, kwds);
683 const int parse_result = _PyArg_ParseTupleAndKeywordsFast(args,
684 kwds_parse,
685 &_parser,
686 pyrna_struct_as_ptr_or_null_parse,
687 &params.window,
688 pyrna_struct_as_ptr_or_null_parse,
689 &params.screen,
690 pyrna_struct_as_ptr_or_null_parse,
691 &params.area,
692 pyrna_struct_as_ptr_or_null_parse,
693 &params.region);
694 Py_DECREF(kwds_parse);
695 if (!parse_result) {
696 Py_DECREF(kwds);
697 return nullptr;
701 bContext *C = static_cast<bContext *>(context_ptr->data);
703 /* Merge existing keys that don't exist in the keywords passed in.
704 * This makes it possible to nest context overrides. */
705 PyObject *context_dict_current = static_cast<PyObject *>(CTX_py_dict_get(C));
706 if (context_dict_current != nullptr) {
707 PyDict_Merge(kwds, context_dict_current, 0);
711 ContextStore ctx_temp = {nullptr};
712 if (params.window.ptr != nullptr) {
713 ctx_temp.win = static_cast<wmWindow *>(params.window.ptr->data);
714 ctx_temp.win_is_set = true;
717 if (params.screen.ptr != nullptr) {
718 ctx_temp.screen = static_cast<bScreen *>(params.screen.ptr->data);
719 ctx_temp.screen_is_set = true;
722 if (params.area.ptr != nullptr) {
723 ctx_temp.area = static_cast<ScrArea *>(params.area.ptr->data);
724 ctx_temp.area_is_set = true;
727 if (params.region.ptr != nullptr) {
728 ctx_temp.region = static_cast<ARegion *>(params.region.ptr->data);
729 ctx_temp.region_is_set = true;
732 BPyContextTempOverride *ret = PyObject_GC_New(BPyContextTempOverride,
733 &BPyContextTempOverride_Type);
734 ret->context = C;
735 ret->ctx_temp = ctx_temp;
736 memset(&ret->ctx_init, 0, sizeof(ret->ctx_init));
738 ret->ctx_temp_orig.screen = nullptr;
740 ret->py_state_context_dict = kwds;
742 PyObject_GC_Track(ret);
744 return (PyObject *)ret;
747 /** \} */
749 /* -------------------------------------------------------------------- */
750 /** \name Public Type Definition
751 * \{ */
753 #if (defined(__GNUC__) && !defined(__clang__))
754 # pragma GCC diagnostic push
755 # pragma GCC diagnostic ignored "-Wcast-function-type"
756 #endif
758 PyMethodDef BPY_rna_context_temp_override_method_def = {
759 "temp_override",
760 (PyCFunction)bpy_context_temp_override,
761 METH_VARARGS | METH_KEYWORDS,
762 bpy_context_temp_override_doc,
765 #if (defined(__GNUC__) && !defined(__clang__))
766 # pragma GCC diagnostic pop
767 #endif
769 void bpy_rna_context_types_init()
771 if (PyType_Ready(&BPyContextTempOverride_Type) < 0) {
772 BLI_assert_unreachable();
773 return;
777 /** \} */