Blender V5.0
bpy_rna_context.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
10
11#include <Python.h>
12
13#include "BLI_listbase.h"
14#include "BLI_utildefines.h"
15
16#include "BKE_context.hh"
17#include "BKE_main.hh"
18#include "BKE_screen.hh"
19#include "BKE_workspace.hh"
20
21#include "WM_api.hh"
22#include "WM_types.hh"
23
24#include "bpy_rna_context.hh"
25
27#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
28
29#include "RNA_access.hh"
30#include "RNA_prototypes.hh"
31
32#include "bpy_rna.hh"
33
34/* -------------------------------------------------------------------- */
37
39{
40 if (screen == nullptr) {
41 return;
42 }
43 if (screen == WM_window_get_active_screen(win)) {
44 return;
45 }
46
47 WorkSpace *workspace;
49 /* Changing workspace instead of just screen as they are tied. */
50 WM_window_set_active_workspace(C, win, workspace);
51 WM_window_set_active_screen(win, workspace, screen);
52}
53
57static bool wm_check_screen_switch_supported(const bScreen *screen)
58{
59 if (screen->temp != 0) {
60 return false;
61 }
63 return false;
64 }
65 return true;
66}
67
68static bool wm_check_window_exists(const Main *bmain, const wmWindow *win)
69{
70 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
71 if (BLI_findindex(&wm->windows, win) != -1) {
72 return true;
73 }
74 }
75 return false;
76}
77
78static bool wm_check_screen_exists(const Main *bmain, const bScreen *screen)
79{
80 if (BLI_findindex(&bmain->screens, screen) != -1) {
81 return true;
82 }
83 return false;
84}
85
86static bool wm_check_area_exists(const wmWindow *win, const bScreen *screen, const ScrArea *area)
87{
88 if (win && (BLI_findindex(&win->global_areas.areabase, area) != -1)) {
89 return true;
90 }
91 if (screen && (BLI_findindex(&screen->areabase, area) != -1)) {
92 return true;
93 }
94 return false;
95}
96
97static bool wm_check_region_exists(const bScreen *screen,
98 const ScrArea *area,
99 const ARegion *region)
100{
101 if (screen && (BLI_findindex(&screen->regionbase, region) != -1)) {
102 return true;
103 }
104 if (area && (BLI_findindex(&area->regionbase, region) != -1)) {
105 return true;
106 }
107 return false;
108}
109
113static void bpy_rna_context_logging_set(bContext *C, bool enable)
114{
115 CTX_member_logging_set(C, enable);
116}
117
119
120/* -------------------------------------------------------------------- */
123
137
139 PyObject_HEAD /* Required Python macro. */
141
144
145 struct {
152
164};
165
167{
168 PyObject_GC_UnTrack(self);
169 Py_CLEAR(self->py_state_context_dict);
170 PyObject_GC_Del(self);
171}
172
174 visitproc visit,
175 void *arg)
176{
177 Py_VISIT(self->py_state_context_dict);
178 return 0;
179}
180
182{
183 Py_CLEAR(self->py_state_context_dict);
184 return 0;
185}
186
188 const Main *bmain,
189 const wmWindow *win,
190 const bScreen *screen,
191 const ScrArea *area,
192 const ARegion *region)
193{
194
195 /* NOTE(@ideasman42): Regarding sanity checks.
196 * There are 3 different situations to be accounted for here regarding overriding windowing data.
197 *
198 * - 1) Nothing is overridden.
199 * Simple, no sanity checks needed.
200 *
201 * - 2) Some members are overridden.
202 * Check the state is consistent (that the region is part the area or screen for example).
203 *
204 * - 3) Some members are overridden *but* the context members are unchanged.
205 * This is a less obvious case which often happens when a Python script copies the context
206 * typically via `context.copy()`, manipulates it and passes it in as keyword arguments.
207 *
208 * A naive approach could be to behave as if these arguments weren't passed in
209 * which would work in many situations however there is a difference
210 * since these members are used to restore the context afterwards.
211 * It's possible a script might use this context-manager to *pin* the context,
212 * running actions that change the context, relying on the context to be restored.
213 *
214 * When error-checking unchanged context members some error checks must be skipped
215 * such as the check to disallow temporary screens since that could break using
216 * `temp_override(..)` running with the current context from a render-window for example.
217 *
218 * In fact all sanity checks could be disabled when the members involved remain unchanged
219 * however it's possible Python scripts corrupt Blender's internal windowing state so keeping
220 * the checks is harmless and alerts developers early on that something is wrong.
221 */
222
223 if (self->ctx_temp.region_is_set && (region != nullptr)) {
224 if (screen == nullptr && area == nullptr) {
225 PyErr_SetString(PyExc_TypeError, "Region set with screen & area set to None");
226 return false;
227 }
228 if (!wm_check_region_exists(screen, area, region)) {
229 PyErr_SetString(PyExc_TypeError, "Region not found in area or screen");
230 return false;
231 }
232 }
233
234 if (self->ctx_temp.area_is_set && (area != nullptr)) {
235 if (win == nullptr && screen == nullptr) {
236 PyErr_SetString(PyExc_TypeError, "Area set with window & screen set to None");
237 return false;
238 }
239 if (!wm_check_area_exists(win, screen, area)) {
240 PyErr_SetString(PyExc_TypeError, "Area not found in screen");
241 return false;
242 }
243 }
244
245 if (self->ctx_temp.screen_is_set && (screen != nullptr)) {
246 if (win == nullptr) {
247 PyErr_SetString(PyExc_TypeError, "Screen set with null window");
248 return false;
249 }
250 if (!wm_check_screen_exists(bmain, screen)) {
251 PyErr_SetString(PyExc_TypeError, "Screen not found");
252 return false;
253 }
254
255 /* Skip some checks when the screen is unchanged. */
256 if (self->ctx_init.screen_is_set) {
257 /* Switching away from a temporary screen isn't supported. */
258 if ((self->ctx_init.screen != nullptr) &&
259 !wm_check_screen_switch_supported(self->ctx_init.screen))
260 {
261 PyErr_SetString(PyExc_TypeError,
262 "Overriding context with an active temporary screen isn't supported");
263 return false;
264 }
266 PyErr_SetString(PyExc_TypeError,
267 "Overriding context with temporary screen isn't supported");
268 return false;
269 }
270 if (BKE_workspace_layout_find_global(bmain, screen, nullptr) == nullptr) {
271 PyErr_SetString(PyExc_TypeError, "Screen has no workspace");
272 return false;
273 }
274
275 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
276 LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
277 if (win_iter == win) {
278 continue;
279 }
280 if (screen == WM_window_get_active_screen(win_iter)) {
281 PyErr_SetString(PyExc_TypeError, "Screen is used by another window");
282 return false;
283 }
284 }
285 }
286 }
287 }
288
289 if (self->ctx_temp.win_is_set && (win != nullptr)) {
290 if (!wm_check_window_exists(bmain, win)) {
291 PyErr_SetString(PyExc_TypeError, "Window not found");
292 return false;
293 }
294 }
295
296 return true;
297}
298
300{
301 bContext *C = self->context;
302 Main *bmain = CTX_data_main(C);
303
304 /* Enable logging for this temporary override context if the user has requested it. */
305 if (self->ctx_temp.use_logging) {
307 }
308
309 /* It's crucial to call #CTX_py_state_pop if this function fails with an error. */
310 CTX_py_state_push(C, &self->py_state, self->py_state_context_dict);
311
312 self->ctx_init.win = CTX_wm_window(C);
313 self->ctx_init.screen = self->ctx_init.win ? WM_window_get_active_screen(self->ctx_init.win) :
315 self->ctx_init.area = CTX_wm_area(C);
316 self->ctx_init.region = CTX_wm_region(C);
317
318 wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
319 bScreen *screen = self->ctx_temp.screen_is_set ? self->ctx_temp.screen : self->ctx_init.screen;
320 ScrArea *area = self->ctx_temp.area_is_set ? self->ctx_temp.area : self->ctx_init.area;
321 ARegion *region = self->ctx_temp.region_is_set ? self->ctx_temp.region : self->ctx_init.region;
322
323 self->ctx_init.win_is_set = (self->ctx_init.win != win);
324 self->ctx_init.screen_is_set = (self->ctx_init.screen != screen);
325 self->ctx_init.area_is_set = (self->ctx_init.area != area);
326 self->ctx_init.region_is_set = (self->ctx_init.region != region);
327
328 /* When the screen isn't passed but a window is, match the screen to the window,
329 * it's important to do this after setting `self->ctx_init.screen_is_set` because the screen is
330 * *not* set, only the window, restoring the window will also restore its screen, see #116297. */
331 if ((self->ctx_temp.win_is_set == true) && (self->ctx_temp.screen_is_set == false)) {
332 screen = win ? WM_window_get_active_screen(win) : nullptr;
333 }
334
335 if (!bpy_rna_context_temp_override_enter_ok_or_error(self, bmain, win, screen, area, region)) {
336 CTX_py_state_pop(C, &self->py_state);
337 return nullptr;
338 }
339
340 /* Manipulate the context (setup). */
341 if (self->ctx_temp.screen_is_set) {
342 self->ctx_temp_orig.screen = WM_window_get_active_screen(win);
343 bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp.screen);
344 }
345
346 /* NOTE: always set these members, even when they are equal to the current values because
347 * setting the window (for example) clears the area & region, setting the area clears the region.
348 * While it would be useful in some cases to leave the context as-is when setting members
349 * to their current values.
350 *
351 * Favor predictable behavior, where setting a member *always* clears the nested
352 * values it contains - no matter the state of the current context.
353 * If this difference is important, the caller can always detect this case and avoid
354 * passing in the context override altogether. */
355
356 if (self->ctx_temp.win_is_set) {
357 CTX_wm_window_set(C, self->ctx_temp.win);
358 }
359 if (self->ctx_temp.screen_is_set) {
360 CTX_wm_screen_set(C, self->ctx_temp.screen);
361 }
362 if (self->ctx_temp.area_is_set) {
363 CTX_wm_area_set(C, self->ctx_temp.area);
364 }
365 if (self->ctx_temp.region_is_set) {
366 CTX_wm_region_set(C, self->ctx_temp.region);
367 }
368
369 return Py_NewRef(self);
370}
371
373 PyObject * /*args*/)
374{
375 bContext *C = self->context;
376
377 Main *bmain = CTX_data_main(C);
378
379 /* Manipulate the context (restore). */
380 if (self->ctx_temp.screen_is_set) {
381 if (self->ctx_temp_orig.screen && wm_check_screen_exists(bmain, self->ctx_temp_orig.screen)) {
382 wmWindow *win = self->ctx_temp.win_is_set ? self->ctx_temp.win : self->ctx_init.win;
383 if (win && wm_check_window_exists(bmain, win)) {
384 /* Disallow switching away from temporary-screens & full-screen areas, while it could be
385 * useful to support this, closing screens uses different and more involved logic
386 * compared with switching between user managed screens, see: #117188. */
388 bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen);
389 }
390 }
391 }
392 }
393
394 /* Account for the window to be freed on file-read,
395 * in this case the window should not be restored, see: #92818.
396 * Also account for other windowing members to be removed on exit,
397 * in this case the context is cleared. */
398 bool do_restore = true;
399
400 /* Restore context members as needed.
401 *
402 * The checks here behaves as follows:
403 * - When `self->ctx_init.win_is_set` is true, the window was changed by the override.
404 * in this case restore the initial window.
405 * - When `self->ctx_temp.win_is_set` is true, the window was set to the current value.
406 * Setting the window (even to the current value) must be accounted for
407 * because setting the window clears the area and the region members,
408 * which must now be restored.
409 *
410 * `is_container_set` is used to detect if nested context members need to be restored.
411 * The comments above refer to the window, it also applies to the screen containing an area
412 * and area which contains a region. */
413 bool is_container_set = false;
414
415 /* Handle Window. */
416 if (do_restore) {
417 if (self->ctx_init.win && !wm_check_window_exists(bmain, self->ctx_init.win)) {
418 CTX_wm_window_set(C, nullptr);
419 do_restore = false;
420 }
421
422 if (do_restore) {
423 if (self->ctx_init.win_is_set) {
424 CTX_wm_window_set(C, self->ctx_init.win);
425 is_container_set = true;
426 }
427 else if (self->ctx_temp.win_is_set) {
428 if (self->ctx_init.win == CTX_wm_window(C)) {
429 is_container_set = true;
430 }
431 else {
432 /* If the context changed, it's incorrect to attempt to restored nested members,
433 * in this case leave the context as-is, see: #119202. */
434 do_restore = false;
435 }
436 }
437 }
438 }
439
440 /* Handle Screen. */
441 if (do_restore) {
442 if (self->ctx_init.screen && !wm_check_screen_exists(bmain, self->ctx_init.screen)) {
443 CTX_wm_screen_set(C, nullptr);
444 do_restore = false;
445 }
446
447 if (do_restore) {
448 if (self->ctx_init.screen_is_set || is_container_set) {
449 CTX_wm_screen_set(C, self->ctx_init.screen);
450 is_container_set = true;
451 }
452 else if (self->ctx_temp.screen_is_set) {
453 if (self->ctx_init.screen == CTX_wm_screen(C)) {
454 is_container_set = true;
455 }
456 else {
457 do_restore = false;
458 }
459 }
460 }
461 }
462
463 /* Handle Area. */
464 if (do_restore) {
465 if (self->ctx_init.area &&
466 !wm_check_area_exists(self->ctx_init.win, self->ctx_init.screen, self->ctx_init.area))
467 {
468 CTX_wm_area_set(C, nullptr);
469 do_restore = false;
470 }
471
472 if (do_restore) {
473 if (self->ctx_init.area_is_set || is_container_set) {
474 CTX_wm_area_set(C, self->ctx_init.area);
475 is_container_set = true;
476 }
477 else if (self->ctx_temp.area_is_set) {
478 if (self->ctx_init.area == CTX_wm_area(C)) {
479 is_container_set = true;
480 }
481 else {
482 do_restore = false;
483 }
484 }
485 }
486 }
487
488 /* Handle Region. */
489 if (do_restore) {
490 if (self->ctx_init.region &&
491 !wm_check_region_exists(self->ctx_init.screen, self->ctx_init.area, self->ctx_init.region))
492 {
493 CTX_wm_region_set(C, nullptr);
494 do_restore = false;
495 }
496
497 if (do_restore) {
498 if (self->ctx_init.region_is_set || is_container_set) {
499 CTX_wm_region_set(C, self->ctx_init.region);
500 is_container_set = true;
501 }
502 /* Enable is there is ever data nested within the region. */
503 else if (false && self->ctx_temp.region_is_set) {
504 if (self->ctx_init.region == CTX_wm_region(C)) {
505 is_container_set = true;
506 }
507 else {
508 do_restore = false;
509 }
510 }
511 }
512 }
513 UNUSED_VARS(is_container_set, do_restore);
514
515 /* Finished restoring the context. */
516
517 /* A copy may have been made when writing context members, see #BPY_context_dict_clear_members */
518 PyObject *context_dict_test = static_cast<PyObject *>(CTX_py_dict_get(C));
519 if (context_dict_test && (context_dict_test != self->py_state_context_dict)) {
520 Py_DECREF(context_dict_test);
521 }
522
523 /* Restore logging state based on the user's preference stored in ctx_init.use_logging. */
524 bpy_rna_context_logging_set(C, self->ctx_init.use_logging);
525
526 CTX_py_state_pop(C, &self->py_state);
527
528 Py_RETURN_NONE;
529}
530
532 PyObject *args,
533 PyObject *kwds)
534{
535 bool enable = true;
536
537 static const char *kwlist[] = {"", nullptr};
538 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&", (char **)kwlist, PyC_ParseBool, &enable)) {
539 return nullptr;
540 }
541
542 self->ctx_temp.use_logging = enable;
543
544 bpy_rna_context_logging_set(self->context, enable);
545
546 Py_RETURN_NONE;
547}
548
549#ifdef __GNUC__
550# ifdef __clang__
551# pragma clang diagnostic push
552# pragma clang diagnostic ignored "-Wcast-function-type"
553# else
554# pragma GCC diagnostic push
555# pragma GCC diagnostic ignored "-Wcast-function-type"
556# endif
557#endif
558
560 {"__enter__", (PyCFunction)bpy_rna_context_temp_override_enter, METH_NOARGS},
561 {"__exit__", (PyCFunction)bpy_rna_context_temp_override_exit, METH_VARARGS},
562 {"logging_set",
564 METH_VARARGS | METH_KEYWORDS},
565 {nullptr},
566};
567
568#ifdef __GNUC__
569# ifdef __clang__
570# pragma clang diagnostic pop
571# else
572# pragma GCC diagnostic pop
573# endif
574#endif
575
576static PyTypeObject BPyContextTempOverride_Type = {
577 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
578 /*tp_name*/ "ContextTempOverride",
579 /*tp_basicsize*/ sizeof(BPyContextTempOverride),
580 /*tp_itemsize*/ 0,
581 /*tp_dealloc*/ (destructor)bpy_rna_context_temp_override_dealloc,
582 /*tp_vectorcall_offset*/ 0,
583 /*tp_getattr*/ nullptr,
584 /*tp_setattr*/ nullptr,
585 /*tp_as_async*/ nullptr,
586 /*tp_repr*/ nullptr,
587 /*tp_as_number*/ nullptr,
588 /*tp_as_sequence*/ nullptr,
589 /*tp_as_mapping*/ nullptr,
590 /*tp_hash*/ nullptr,
591 /*tp_call*/ nullptr,
592 /*tp_str*/ nullptr,
593 /*tp_getattro*/ nullptr,
594 /*tp_setattro*/ nullptr,
595 /*tp_as_buffer*/ nullptr,
596 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
597 /*tp_doc*/ nullptr,
598 /*tp_traverse*/ (traverseproc)bpy_rna_context_temp_override_traverse,
599 /*tp_clear*/ (inquiry)bpy_rna_context_temp_override_clear,
600 /*tp_richcompare*/ nullptr,
601 /*tp_weaklistoffset*/ 0,
602 /*tp_iter*/ nullptr,
603 /*tp_iternext*/ nullptr,
605 /*tp_members*/ nullptr,
606 /*tp_getset*/ nullptr,
607 /*tp_base*/ nullptr,
608 /*tp_dict*/ nullptr,
609 /*tp_descr_get*/ nullptr,
610 /*tp_descr_set*/ nullptr,
611 /*tp_dictoffset*/ 0,
612 /*tp_init*/ nullptr,
613 /*tp_alloc*/ nullptr,
614 /*tp_new*/ nullptr,
615 /*tp_free*/ nullptr,
616 /*tp_is_gc*/ nullptr,
617 /*tp_bases*/ nullptr,
618 /*tp_mro*/ nullptr,
619 /*tp_cache*/ nullptr,
620 /*tp_subclasses*/ nullptr,
621 /*tp_weaklist*/ nullptr,
622 /*tp_del*/ nullptr,
623 /*tp_version_tag*/ 0,
624 /*tp_finalize*/ nullptr,
625 /*tp_vectorcall*/ nullptr,
626};
627
629
630/* -------------------------------------------------------------------- */
633
634static PyObject *bpy_context_temp_override_extract_known_args(const char *const *kwds_static,
635 PyObject *kwds)
636{
637 PyObject *kwds_parse = PyDict_New();
638 for (int i = 0; kwds_static[i]; i++) {
639 PyObject *key = PyUnicode_FromString(kwds_static[i]);
640 PyObject *val;
641
642#if PY_VERSION_HEX >= 0x030d0000
643 switch (PyDict_Pop(kwds, key, &val)) {
644 case 1: {
645 if (PyDict_SetItem(kwds_parse, key, val) == -1) {
647 }
648 Py_DECREF(val);
649 break;
650 }
651 case -1: {
652 /* Not expected, but allow for an error. */
653 BLI_assert(false);
654 PyErr_Clear();
655 break;
656 }
657 }
658#else /* Remove when Python 3.12 support is dropped. */
659 PyObject *sentinel = Py_Ellipsis;
660 val = _PyDict_Pop(kwds, key, sentinel);
661 if (val != sentinel) {
662 if (PyDict_SetItem(kwds_parse, key, val) == -1) {
664 }
665 }
666 Py_DECREF(val);
667#endif
668
669 Py_DECREF(key);
670 }
671 return kwds_parse;
672}
673
674/* NOTE(@ideasman42): `ContextTempOverride` isn't accessible from (without creating an instance),
675 * it should be exposed although it doesn't seem especially important either. */
677 /* Wrap. */
678 bpy_context_temp_override_doc,
679 ".. method:: temp_override(*, window=None, screen=None, area=None, region=None, **keywords)\n"
680 "\n"
681 " Context manager to temporarily override members in the context.\n"
682 "\n"
683 " :arg window: Window override or None.\n"
684 " :type window: :class:`bpy.types.Window`\n"
685 " :arg screen: Screen override or None.\n"
686 "\n"
687 " .. note:: Switching to or away from full-screen areas & temporary screens "
688 "isn't supported. Passing in these screens will raise an exception, "
689 "actions that leave the context such screens won't restore the prior screen.\n"
690 "\n"
691 " .. note:: Changing the screen has wider implications "
692 "than other arguments as it will also change the works-space "
693 "and potentially the scene (when pinned).\n"
694 "\n"
695 " :type screen: :class:`bpy.types.Screen`\n"
696 " :arg area: Area override or None.\n"
697 " :type area: :class:`bpy.types.Area`\n"
698 " :arg region: Region override or None.\n"
699 " :type region: :class:`bpy.types.Region`\n"
700 " :arg keywords: Additional keywords override context members.\n"
701 " :return: The context manager .\n"
702 " :rtype: ContextTempOverride\n");
703static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds)
704{
705 const PointerRNA *context_ptr = pyrna_struct_as_ptr(self, &RNA_Context);
706 if (context_ptr == nullptr) {
707 return nullptr;
708 }
709
710 if (kwds == nullptr) {
711 /* While this is effectively NOP, support having no keywords as it's more involved
712 * to return an alternative (dummy) context manager. */
713 }
714 else {
715 /* Needed because the keywords copied into `kwds_parse` could contain anything.
716 * As the types of keys aren't checked. */
717 if (!PyArg_ValidateKeywordArguments(kwds)) {
718 return nullptr;
719 }
720 }
721
722 struct {
723 BPy_StructRNA_Parse window;
724 BPy_StructRNA_Parse screen;
726 BPy_StructRNA_Parse region;
727 } params{};
728 params.window.type = &RNA_Window;
729 params.screen.type = &RNA_Screen;
730 params.area.type = &RNA_Area;
731 params.region.type = &RNA_Region;
732
733 static const char *const _keywords[] = {
734 "window",
735 "screen",
736 "area",
737 "region",
738 nullptr,
739 };
740 static _PyArg_Parser _parser = {
742 "|$" /* Optional, keyword only arguments. */
743 "O&" /* `window` */
744 "O&" /* `screen` */
745 "O&" /* `area` */
746 "O&" /* `region` */
747 ":temp_override",
748 _keywords,
749 nullptr,
750 };
751 /* Parse known keywords, the remaining keywords are set using #CTX_py_state_push. */
752 kwds = kwds ? PyDict_Copy(kwds) : PyDict_New();
753 {
754 PyObject *kwds_parse = bpy_context_temp_override_extract_known_args(_keywords, kwds);
755 const int parse_result = _PyArg_ParseTupleAndKeywordsFast(args,
756 kwds_parse,
757 &_parser,
759 &params.window,
761 &params.screen,
763 &params.area,
765 &params.region);
766 Py_DECREF(kwds_parse);
767 if (!parse_result) {
768 Py_DECREF(kwds);
769 return nullptr;
770 }
771 }
772
773 bContext *C = static_cast<bContext *>(context_ptr->data);
774 {
775 /* Merge existing keys that don't exist in the keywords passed in.
776 * This makes it possible to nest context overrides. */
777 PyObject *context_dict_current = static_cast<PyObject *>(CTX_py_dict_get(C));
778 if (context_dict_current != nullptr) {
779 PyDict_Merge(kwds, context_dict_current, 0);
780 }
781 }
782
783 ContextStore ctx_temp = {nullptr};
784 if (params.window.ptr != nullptr) {
785 ctx_temp.win = static_cast<wmWindow *>(params.window.ptr->data);
786 ctx_temp.win_is_set = true;
787 }
788
789 if (params.screen.ptr != nullptr) {
790 ctx_temp.screen = static_cast<bScreen *>(params.screen.ptr->data);
791 ctx_temp.screen_is_set = true;
792 }
793
794 if (params.area.ptr != nullptr) {
795 ctx_temp.area = static_cast<ScrArea *>(params.area.ptr->data);
796 ctx_temp.area_is_set = true;
797 }
798
799 if (params.region.ptr != nullptr) {
800 ctx_temp.region = static_cast<ARegion *>(params.region.ptr->data);
801 ctx_temp.region_is_set = true;
802 }
803
806 ret->context = C;
807 ret->ctx_temp = ctx_temp;
808 memset(&ret->ctx_init, 0, sizeof(ret->ctx_init));
809
810 ret->ctx_temp_orig.screen = nullptr;
811
812 ret->py_state_context_dict = kwds;
813
814 PyObject_GC_Track(ret);
815
816 return (PyObject *)ret;
817}
818
820
821/* -------------------------------------------------------------------- */
824
825#ifdef __GNUC__
826# ifdef __clang__
827# pragma clang diagnostic push
828# pragma clang diagnostic ignored "-Wcast-function-type"
829# else
830# pragma GCC diagnostic push
831# pragma GCC diagnostic ignored "-Wcast-function-type"
832# endif
833#endif
834
836 "temp_override",
837 (PyCFunction)bpy_context_temp_override,
838 METH_VARARGS | METH_KEYWORDS,
839 bpy_context_temp_override_doc,
840};
841
842#ifdef __GNUC__
843# ifdef __clang__
844# pragma clang diagnostic pop
845# else
846# pragma GCC diagnostic pop
847# endif
848#endif
849
851{
852 if (PyType_Ready(&BPyContextTempOverride_Type) < 0) {
854 return;
855 }
856}
857
void CTX_member_logging_set(bContext *C, bool enable)
void CTX_py_state_push(bContext *C, bContext_PyState *pystate, void *value)
bScreen * CTX_wm_screen(const bContext *C)
void CTX_py_state_pop(bContext *C, bContext_PyState *pystate)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
void CTX_wm_screen_set(bContext *C, bScreen *screen)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
ARegion * CTX_wm_region(const bContext *C)
void * CTX_py_dict_get(const bContext *C)
bool BKE_screen_is_fullscreen_area(const bScreen *screen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition screen.cc:1049
WorkSpaceLayout * BKE_workspace_layout_find_global(const Main *bmain, const bScreen *screen, WorkSpace **r_workspace) ATTR_NONNULL(1
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
#define UNUSED_VARS(...)
#define C
Definition RandGen.cpp:29
PyObject * self
const PointerRNA * pyrna_struct_as_ptr(PyObject *py_obj, const StructRNA *srna)
Definition bpy_rna.cc:9012
int pyrna_struct_as_ptr_or_null_parse(PyObject *o, void *p)
Definition bpy_rna.cc:9045
static PyObject * bpy_rna_context_temp_override_enter(BPyContextTempOverride *self)
static PyTypeObject BPyContextTempOverride_Type
static bool wm_check_area_exists(const wmWindow *win, const bScreen *screen, const ScrArea *area)
PyDoc_STRVAR(bpy_context_temp_override_doc, ".. method:: temp_override(*, window=None, screen=None, area=None, region=None, **keywords)\n" "\n" " Context manager to temporarily override members in the context.\n" "\n" " :arg window: Window override or None.\n" " :type window: :class:`bpy.types.Window`\n" " :arg screen: Screen override or None.\n" "\n" " .. note:: Switching to or away from full-screen areas & temporary screens " "isn't supported. Passing in these screens will raise an exception, " "actions that leave the context such screens won't restore the prior screen.\n" "\n" " .. note:: Changing the screen has wider implications " "than other arguments as it will also change the works-space " "and potentially the scene (when pinned).\n" "\n" " :type screen: :class:`bpy.types.Screen`\n" " :arg area: Area override or None.\n" " :type area: :class:`bpy.types.Area`\n" " :arg region: Region override or None.\n" " :type region: :class:`bpy.types.Region`\n" " :arg keywords: Additional keywords override context members.\n" " :return: The context manager .\n" " :rtype: ContextTempOverride\n")
static PyObject * bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds)
static bool wm_check_region_exists(const bScreen *screen, const ScrArea *area, const ARegion *region)
static void bpy_rna_context_temp_override_dealloc(BPyContextTempOverride *self)
static bool wm_check_window_exists(const Main *bmain, const wmWindow *win)
static PyObject * bpy_rna_context_temp_override_logging_set(BPyContextTempOverride *self, PyObject *args, PyObject *kwds)
static void bpy_rna_context_logging_set(bContext *C, bool enable)
static bool bpy_rna_context_temp_override_enter_ok_or_error(const BPyContextTempOverride *self, const Main *bmain, const wmWindow *win, const bScreen *screen, const ScrArea *area, const ARegion *region)
static PyObject * bpy_context_temp_override_extract_known_args(const char *const *kwds_static, PyObject *kwds)
static bool wm_check_screen_switch_supported(const bScreen *screen)
static bool wm_check_screen_exists(const Main *bmain, const bScreen *screen)
static PyObject * bpy_rna_context_temp_override_exit(BPyContextTempOverride *self, PyObject *)
void bpy_rna_context_types_init()
static int bpy_rna_context_temp_override_traverse(BPyContextTempOverride *self, visitproc visit, void *arg)
static PyMethodDef bpy_rna_context_temp_override_methods[]
static void bpy_rna_context_temp_set_screen_for_window(bContext *C, wmWindow *win, bScreen *screen)
PyMethodDef BPY_rna_context_temp_override_method_def
static int bpy_rna_context_temp_override_clear(BPyContextTempOverride *self)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
Py_DECREF(oname)
#define PY_ARG_PARSER_HEAD_COMPAT()
return ret
PyObject_HEAD bContext * context
struct BPyContextTempOverride::@304241065162324270114022056126054014113033266215 ctx_temp_orig
bContext_PyState py_state
ListBase wm
Definition BKE_main.hh:307
ListBase screens
Definition BKE_main.hh:292
void * data
Definition RNA_types.hh:53
ListBase areabase
ListBase regionbase
ListBase regionbase
ListBase areabase
ScrAreaMap global_areas
i
Definition text_draw.cc:230
void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
bScreen * WM_window_get_active_screen(const wmWindow *win)