Blender V4.5
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
26#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
27
28#include "RNA_access.hh"
29#include "RNA_prototypes.hh"
30
31#include "bpy_rna.hh"
32
33/* -------------------------------------------------------------------- */
36
38{
39 if (screen == nullptr) {
40 return;
41 }
42 if (screen == WM_window_get_active_screen(win)) {
43 return;
44 }
45
46 WorkSpace *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);
51}
52
56static bool wm_check_screen_switch_supported(const bScreen *screen)
57{
58 if (screen->temp != 0) {
59 return false;
60 }
62 return false;
63 }
64 return true;
65}
66
67static bool wm_check_window_exists(const Main *bmain, const wmWindow *win)
68{
69 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
70 if (BLI_findindex(&wm->windows, win) != -1) {
71 return true;
72 }
73 }
74 return false;
75}
76
77static bool wm_check_screen_exists(const Main *bmain, const bScreen *screen)
78{
79 if (BLI_findindex(&bmain->screens, screen) != -1) {
80 return true;
81 }
82 return false;
83}
84
85static bool wm_check_area_exists(const wmWindow *win, const bScreen *screen, const ScrArea *area)
86{
87 if (win && (BLI_findindex(&win->global_areas.areabase, area) != -1)) {
88 return true;
89 }
90 if (screen && (BLI_findindex(&screen->areabase, area) != -1)) {
91 return true;
92 }
93 return false;
94}
95
96static bool wm_check_region_exists(const bScreen *screen,
97 const ScrArea *area,
98 const ARegion *region)
99{
100 if (screen && (BLI_findindex(&screen->regionbase, region) != -1)) {
101 return true;
102 }
103 if (area && (BLI_findindex(&area->regionbase, region) != -1)) {
104 return true;
105 }
106 return false;
107}
108
110
111/* -------------------------------------------------------------------- */
114
125
127 PyObject_HEAD /* Required Python macro. */
129
132
133 struct {
140
152};
153
155{
156 PyObject_GC_UnTrack(self);
157 Py_CLEAR(self->py_state_context_dict);
158 PyObject_GC_Del(self);
159}
160
162 visitproc visit,
163 void *arg)
164{
165 Py_VISIT(self->py_state_context_dict);
166 return 0;
167}
168
170{
171 Py_CLEAR(self->py_state_context_dict);
172 return 0;
173}
174
176 const Main *bmain,
177 const wmWindow *win,
178 const bScreen *screen,
179 const ScrArea *area,
180 const ARegion *region)
181{
182
183 /* NOTE(@ideasman42): Regarding sanity checks.
184 * There are 3 different situations to be accounted for here regarding overriding windowing data.
185 *
186 * - 1) Nothing is overridden.
187 * Simple, no sanity checks needed.
188 *
189 * - 2) Some members are overridden.
190 * Check the state is consistent (that the region is part the area or screen for example).
191 *
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.
195 *
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.
201 *
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 example.
205 *
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.
209 */
210
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;
215 }
216 if (!wm_check_region_exists(screen, area, region)) {
217 PyErr_SetString(PyExc_TypeError, "Region not found in area or screen");
218 return false;
219 }
220 }
221
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;
226 }
227 if (!wm_check_area_exists(win, screen, area)) {
228 PyErr_SetString(PyExc_TypeError, "Area not found in screen");
229 return false;
230 }
231 }
232
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;
237 }
238 if (!wm_check_screen_exists(bmain, screen)) {
239 PyErr_SetString(PyExc_TypeError, "Screen not found");
240 return false;
241 }
242
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))
248 {
249 PyErr_SetString(PyExc_TypeError,
250 "Overriding context with an active temporary screen isn't supported");
251 return false;
252 }
254 PyErr_SetString(PyExc_TypeError,
255 "Overriding context with temporary screen isn't supported");
256 return false;
257 }
258 if (BKE_workspace_layout_find_global(bmain, screen, nullptr) == nullptr) {
259 PyErr_SetString(PyExc_TypeError, "Screen has no workspace");
260 return false;
261 }
262
263 LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
264 LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) {
265 if (win_iter == win) {
266 continue;
267 }
268 if (screen == WM_window_get_active_screen(win_iter)) {
269 PyErr_SetString(PyExc_TypeError, "Screen is used by another window");
270 return false;
271 }
272 }
273 }
274 }
275 }
276
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;
281 }
282 }
283
284 return true;
285}
286
288{
289 bContext *C = self->context;
290 Main *bmain = CTX_data_main(C);
291
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);
294
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) :
298 self->ctx_init.area = CTX_wm_area(C);
299 self->ctx_init.region = CTX_wm_region(C);
300
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;
305
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);
310
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;
316 }
317
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;
321 }
322
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);
327 }
328
329 /* NOTE: always set these members, even when they are equal to the current values because
330 * setting the window (for example) 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.
333 *
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. */
338
339 if (self->ctx_temp.win_is_set) {
340 CTX_wm_window_set(C, self->ctx_temp.win);
341 }
342 if (self->ctx_temp.screen_is_set) {
343 CTX_wm_screen_set(C, self->ctx_temp.screen);
344 }
345 if (self->ctx_temp.area_is_set) {
346 CTX_wm_area_set(C, self->ctx_temp.area);
347 }
348 if (self->ctx_temp.region_is_set) {
349 CTX_wm_region_set(C, self->ctx_temp.region);
350 }
351
352 Py_RETURN_NONE;
353}
354
356 PyObject * /*args*/)
357{
358 bContext *C = self->context;
359
360 Main *bmain = CTX_data_main(C);
361
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. */
371 bpy_rna_context_temp_set_screen_for_window(C, win, self->ctx_temp_orig.screen);
372 }
373 }
374 }
375 }
376
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;
382
383 /* Restore context members as needed.
384 *
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.
392 *
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;
397
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;
403 }
404
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;
409 }
410 else if (self->ctx_temp.win_is_set) {
411 if (self->ctx_init.win == CTX_wm_window(C)) {
412 is_container_set = true;
413 }
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;
418 }
419 }
420 }
421 }
422
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;
428 }
429
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;
434 }
435 else if (self->ctx_temp.screen_is_set) {
436 if (self->ctx_init.screen == CTX_wm_screen(C)) {
437 is_container_set = true;
438 }
439 else {
440 do_restore = false;
441 }
442 }
443 }
444 }
445
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))
450 {
451 CTX_wm_area_set(C, nullptr);
452 do_restore = false;
453 }
454
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;
459 }
460 else if (self->ctx_temp.area_is_set) {
461 if (self->ctx_init.area == CTX_wm_area(C)) {
462 is_container_set = true;
463 }
464 else {
465 do_restore = false;
466 }
467 }
468 }
469 }
470
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))
475 {
476 CTX_wm_region_set(C, nullptr);
477 do_restore = false;
478 }
479
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;
484 }
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;
489 }
490 else {
491 do_restore = false;
492 }
493 }
494 }
495 }
496 UNUSED_VARS(is_container_set, do_restore);
497
498 /* Finished restoring the context. */
499
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);
504 }
505 CTX_py_state_pop(C, &self->py_state);
506
507 Py_RETURN_NONE;
508}
509
510#ifdef __GNUC__
511# ifdef __clang__
512# pragma clang diagnostic push
513# pragma clang diagnostic ignored "-Wcast-function-type"
514# else
515# pragma GCC diagnostic push
516# pragma GCC diagnostic ignored "-Wcast-function-type"
517# endif
518#endif
519
521 {"__enter__", (PyCFunction)bpy_rna_context_temp_override_enter, METH_NOARGS},
522 {"__exit__", (PyCFunction)bpy_rna_context_temp_override_exit, METH_VARARGS},
523 {nullptr},
524};
525
526#ifdef __GNUC__
527# ifdef __clang__
528# pragma clang diagnostic pop
529# else
530# pragma GCC diagnostic pop
531# endif
532#endif
533
534static PyTypeObject BPyContextTempOverride_Type = {
535 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
536 /*tp_name*/ "ContextTempOverride",
537 /*tp_basicsize*/ sizeof(BPyContextTempOverride),
538 /*tp_itemsize*/ 0,
539 /*tp_dealloc*/ (destructor)bpy_rna_context_temp_override_dealloc,
540 /*tp_vectorcall_offset*/ 0,
541 /*tp_getattr*/ nullptr,
542 /*tp_setattr*/ nullptr,
543 /*tp_as_async*/ nullptr,
544 /*tp_repr*/ nullptr,
545 /*tp_as_number*/ nullptr,
546 /*tp_as_sequence*/ nullptr,
547 /*tp_as_mapping*/ nullptr,
548 /*tp_hash*/ nullptr,
549 /*tp_call*/ nullptr,
550 /*tp_str*/ nullptr,
551 /*tp_getattro*/ nullptr,
552 /*tp_setattro*/ nullptr,
553 /*tp_as_buffer*/ nullptr,
554 /*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
555 /*tp_doc*/ nullptr,
556 /*tp_traverse*/ (traverseproc)bpy_rna_context_temp_override_traverse,
557 /*tp_clear*/ (inquiry)bpy_rna_context_temp_override_clear,
558 /*tp_richcompare*/ nullptr,
559 /*tp_weaklistoffset*/ 0,
560 /*tp_iter*/ nullptr,
561 /*tp_iternext*/ nullptr,
563 /*tp_members*/ nullptr,
564 /*tp_getset*/ nullptr,
565 /*tp_base*/ nullptr,
566 /*tp_dict*/ nullptr,
567 /*tp_descr_get*/ nullptr,
568 /*tp_descr_set*/ nullptr,
569 /*tp_dictoffset*/ 0,
570 /*tp_init*/ nullptr,
571 /*tp_alloc*/ nullptr,
572 /*tp_new*/ nullptr,
573 /*tp_free*/ nullptr,
574 /*tp_is_gc*/ nullptr,
575 /*tp_bases*/ nullptr,
576 /*tp_mro*/ nullptr,
577 /*tp_cache*/ nullptr,
578 /*tp_subclasses*/ nullptr,
579 /*tp_weaklist*/ nullptr,
580 /*tp_del*/ nullptr,
581 /*tp_version_tag*/ 0,
582 /*tp_finalize*/ nullptr,
583 /*tp_vectorcall*/ nullptr,
584};
585
587
588/* -------------------------------------------------------------------- */
591
592static PyObject *bpy_context_temp_override_extract_known_args(const char *const *kwds_static,
593 PyObject *kwds)
594{
595 PyObject *sentinel = Py_Ellipsis;
596 PyObject *kwds_parse = PyDict_New();
597 for (int i = 0; kwds_static[i]; i++) {
598 PyObject *key = PyUnicode_FromString(kwds_static[i]);
599 PyObject *val = _PyDict_Pop(kwds, key, sentinel);
600 if (val != sentinel) {
601 if (PyDict_SetItem(kwds_parse, key, val) == -1) {
603 }
604 }
605 Py_DECREF(key);
606 Py_DECREF(val);
607 }
608 return kwds_parse;
609}
610
611/* NOTE(@ideasman42): `ContextTempOverride` isn't accessible from (without creating an instance),
612 * it should be exposed although it doesn't seem especially important either. */
614 /* Wrap. */
615 bpy_context_temp_override_doc,
616 ".. method:: temp_override(*, window=None, area=None, region=None, **keywords)\n"
617 "\n"
618 " Context manager to temporarily override members in the context.\n"
619 "\n"
620 " :arg window: Window override or None.\n"
621 " :type window: :class:`bpy.types.Window`\n"
622 " :arg screen: Screen override or None.\n"
623 "\n"
624 " .. note:: Switching to or away from full-screen areas & temporary screens "
625 "isn't supported. Passing in these screens will raise an exception, "
626 "actions that leave the context such screens won't restore the prior screen.\n"
627 "\n"
628 " .. note:: Changing the screen has wider implications "
629 "than other arguments as it will also change the works-space "
630 "and potentially the scene (when pinned).\n"
631 "\n"
632 " :type screen: :class:`bpy.types.Screen`\n"
633 " :arg area: Area override or None.\n"
634 " :type area: :class:`bpy.types.Area`\n"
635 " :arg region: Region override or None.\n"
636 " :type region: :class:`bpy.types.Region`\n"
637 " :arg keywords: Additional keywords override context members.\n"
638 " :return: The context manager .\n"
639 " :rtype: ContextTempOverride\n");
640static PyObject *bpy_context_temp_override(PyObject *self, PyObject *args, PyObject *kwds)
641{
642 const PointerRNA *context_ptr = pyrna_struct_as_ptr(self, &RNA_Context);
643 if (context_ptr == nullptr) {
644 return nullptr;
645 }
646
647 if (kwds == nullptr) {
648 /* While this is effectively NOP, support having no keywords as it's more involved
649 * to return an alternative (dummy) context manager. */
650 }
651 else {
652 /* Needed because the keywords copied into `kwds_parse` could contain anything.
653 * As the types of keys aren't checked. */
654 if (!PyArg_ValidateKeywordArguments(kwds)) {
655 return nullptr;
656 }
657 }
658
659 struct {
660 BPy_StructRNA_Parse window;
661 BPy_StructRNA_Parse screen;
663 BPy_StructRNA_Parse region;
664 } params{};
665 params.window.type = &RNA_Window;
666 params.screen.type = &RNA_Screen;
667 params.area.type = &RNA_Area;
668 params.region.type = &RNA_Region;
669
670 static const char *const _keywords[] = {
671 "window",
672 "screen",
673 "area",
674 "region",
675 nullptr,
676 };
677 static _PyArg_Parser _parser = {
679 "|$" /* Optional, keyword only arguments. */
680 "O&" /* `window` */
681 "O&" /* `screen` */
682 "O&" /* `area` */
683 "O&" /* `region` */
684 ":temp_override",
685 _keywords,
686 nullptr,
687 };
688 /* Parse known keywords, the remaining keywords are set using #CTX_py_state_push. */
689 kwds = kwds ? PyDict_Copy(kwds) : PyDict_New();
690 {
691 PyObject *kwds_parse = bpy_context_temp_override_extract_known_args(_keywords, kwds);
692 const int parse_result = _PyArg_ParseTupleAndKeywordsFast(args,
693 kwds_parse,
694 &_parser,
696 &params.window,
698 &params.screen,
700 &params.area,
702 &params.region);
703 Py_DECREF(kwds_parse);
704 if (!parse_result) {
705 Py_DECREF(kwds);
706 return nullptr;
707 }
708 }
709
710 bContext *C = static_cast<bContext *>(context_ptr->data);
711 {
712 /* Merge existing keys that don't exist in the keywords passed in.
713 * This makes it possible to nest context overrides. */
714 PyObject *context_dict_current = static_cast<PyObject *>(CTX_py_dict_get(C));
715 if (context_dict_current != nullptr) {
716 PyDict_Merge(kwds, context_dict_current, 0);
717 }
718 }
719
720 ContextStore ctx_temp = {nullptr};
721 if (params.window.ptr != nullptr) {
722 ctx_temp.win = static_cast<wmWindow *>(params.window.ptr->data);
723 ctx_temp.win_is_set = true;
724 }
725
726 if (params.screen.ptr != nullptr) {
727 ctx_temp.screen = static_cast<bScreen *>(params.screen.ptr->data);
728 ctx_temp.screen_is_set = true;
729 }
730
731 if (params.area.ptr != nullptr) {
732 ctx_temp.area = static_cast<ScrArea *>(params.area.ptr->data);
733 ctx_temp.area_is_set = true;
734 }
735
736 if (params.region.ptr != nullptr) {
737 ctx_temp.region = static_cast<ARegion *>(params.region.ptr->data);
738 ctx_temp.region_is_set = true;
739 }
740
743 ret->context = C;
744 ret->ctx_temp = ctx_temp;
745 memset(&ret->ctx_init, 0, sizeof(ret->ctx_init));
746
747 ret->ctx_temp_orig.screen = nullptr;
748
749 ret->py_state_context_dict = kwds;
750
751 PyObject_GC_Track(ret);
752
753 return (PyObject *)ret;
754}
755
757
758/* -------------------------------------------------------------------- */
761
762#ifdef __GNUC__
763# ifdef __clang__
764# pragma clang diagnostic push
765# pragma clang diagnostic ignored "-Wcast-function-type"
766# else
767# pragma GCC diagnostic push
768# pragma GCC diagnostic ignored "-Wcast-function-type"
769# endif
770#endif
771
773 "temp_override",
774 (PyCFunction)bpy_context_temp_override,
775 METH_VARARGS | METH_KEYWORDS,
776 bpy_context_temp_override_doc,
777};
778
779#ifdef __GNUC__
780# ifdef __clang__
781# pragma clang diagnostic pop
782# else
783# pragma GCC diagnostic pop
784# endif
785#endif
786
788{
789 if (PyType_Ready(&BPyContextTempOverride_Type) < 0) {
791 return;
792 }
793}
794
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:1043
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
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:8900
int pyrna_struct_as_ptr_or_null_parse(PyObject *o, void *p)
Definition bpy_rna.cc:8933
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)
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 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)
PyDoc_STRVAR(bpy_context_temp_override_doc, ".. method:: temp_override(*, window=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_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]
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
return ret
struct BPyContextTempOverride::@354233257271307345007214273262002220225061276230 ctx_temp_orig
PyObject_HEAD bContext * context
bContext_PyState py_state
ListBase wm
Definition BKE_main.hh:276
ListBase screens
Definition BKE_main.hh:261
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)