1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2007 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 *
23 * Internal functions for managing UI registrable types (operator, UI and menu types).
24 *
25 * Also Blender's main event loop (WM_main).
26 */
27
28 #include <stddef.h>
29 #include <string.h>
30
31 #include "BLI_sys_types.h"
32
33 #include "DNA_windowmanager_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39
40 #include "BLT_translation.h"
41
42 #include "BKE_context.h"
43 #include "BKE_global.h"
44 #include "BKE_idprop.h"
45 #include "BKE_idtype.h"
46 #include "BKE_lib_id.h"
47 #include "BKE_lib_query.h"
48 #include "BKE_main.h"
49 #include "BKE_report.h"
50 #include "BKE_screen.h"
51 #include "BKE_workspace.h"
52
53 #include "WM_api.h"
54 #include "WM_message.h"
55 #include "WM_types.h"
56 #include "wm.h"
57 #include "wm_draw.h"
58 #include "wm_event_system.h"
59 #include "wm_window.h"
60 #ifdef WITH_XR_OPENXR
61 # include "wm_xr.h"
62 #endif
63
64 #include "BKE_undo_system.h"
65 #include "ED_screen.h"
66
67 #ifdef WITH_PYTHON
68 # include "BPY_extern.h"
69 # include "BPY_extern_run.h"
70 #endif
71
72 /* ****************************************************** */
73
window_manager_free_data(ID * id)74 static void window_manager_free_data(ID *id)
75 {
76 wm_close_and_free(NULL, (wmWindowManager *)id);
77 }
78
window_manager_foreach_id(ID * id,LibraryForeachIDData * data)79 static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data)
80 {
81 wmWindowManager *wm = (wmWindowManager *)id;
82
83 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
84 BKE_LIB_FOREACHID_PROCESS(data, win->scene, IDWALK_CB_USER_ONE);
85
86 /* This pointer can be NULL during old files reading, better be safe than sorry. */
87 if (win->workspace_hook != NULL) {
88 ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook);
89 BKE_LIB_FOREACHID_PROCESS_ID(data, workspace, IDWALK_CB_NOP);
90 /* Allow callback to set a different workspace. */
91 BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace);
92 }
93 if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) {
94 LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
95 BKE_screen_foreach_id_screen_area(data, area);
96 }
97 }
98 }
99 }
100
101 IDTypeInfo IDType_ID_WM = {
102 .id_code = ID_WM,
103 .id_filter = 0,
104 .main_listbase_index = INDEX_ID_WM,
105 .struct_size = sizeof(wmWindowManager),
106 .name = "WindowManager",
107 .name_plural = "window_managers",
108 .translation_context = BLT_I18NCONTEXT_ID_WINDOWMANAGER,
109 .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL |
110 IDTYPE_FLAGS_NO_ANIMDATA,
111
112 .init_data = NULL,
113 .copy_data = NULL,
114 .free_data = window_manager_free_data,
115 .make_local = NULL,
116 .foreach_id = window_manager_foreach_id,
117 .foreach_cache = NULL,
118
119 .blend_write = NULL,
120 .blend_read_data = NULL,
121 .blend_read_lib = NULL,
122 .blend_read_expand = NULL,
123 };
124
125 #define MAX_OP_REGISTERED 32
126
WM_operator_free(wmOperator * op)127 void WM_operator_free(wmOperator *op)
128 {
129
130 #ifdef WITH_PYTHON
131 if (op->py_instance) {
132 /* Do this first in case there are any __del__ functions or similar that use properties. */
133 BPY_DECREF_RNA_INVALIDATE(op->py_instance);
134 }
135 #endif
136
137 if (op->ptr) {
138 op->properties = op->ptr->data;
139 MEM_freeN(op->ptr);
140 }
141
142 if (op->properties) {
143 IDP_FreeProperty(op->properties);
144 }
145
146 if (op->reports && (op->reports->flag & RPT_FREE)) {
147 BKE_reports_clear(op->reports);
148 MEM_freeN(op->reports);
149 }
150
151 if (op->macro.first) {
152 wmOperator *opm, *opmnext;
153 for (opm = op->macro.first; opm; opm = opmnext) {
154 opmnext = opm->next;
155 WM_operator_free(opm);
156 }
157 }
158
159 MEM_freeN(op);
160 }
161
WM_operator_free_all_after(wmWindowManager * wm,struct wmOperator * op)162 void WM_operator_free_all_after(wmWindowManager *wm, struct wmOperator *op)
163 {
164 op = op->next;
165 while (op != NULL) {
166 wmOperator *op_next = op->next;
167 BLI_remlink(&wm->operators, op);
168 WM_operator_free(op);
169 op = op_next;
170 }
171 }
172
173 /**
174 * Use with extreme care!,
175 * properties, custom-data etc - must be compatible.
176 *
177 * \param op: Operator to assign the type to.
178 * \param ot: Operator type to assign.
179 */
WM_operator_type_set(wmOperator * op,wmOperatorType * ot)180 void WM_operator_type_set(wmOperator *op, wmOperatorType *ot)
181 {
182 /* Not supported for Python. */
183 BLI_assert(op->py_instance == NULL);
184
185 op->type = ot;
186 op->ptr->type = ot->srna;
187
188 /* Ensure compatible properties. */
189 if (op->properties) {
190 PointerRNA ptr;
191 WM_operator_properties_create_ptr(&ptr, ot);
192
193 WM_operator_properties_default(&ptr, false);
194
195 if (ptr.data) {
196 IDP_SyncGroupTypes(op->properties, ptr.data, true);
197 }
198
199 WM_operator_properties_free(&ptr);
200 }
201 }
202
wm_reports_free(wmWindowManager * wm)203 static void wm_reports_free(wmWindowManager *wm)
204 {
205 BKE_reports_clear(&wm->reports);
206 WM_event_remove_timer(wm, NULL, wm->reports.reporttimer);
207 }
208
209 /* All operations get registered in the windowmanager here. */
210 /* Called on event handling by event_system.c. */
wm_operator_register(bContext * C,wmOperator * op)211 void wm_operator_register(bContext *C, wmOperator *op)
212 {
213 wmWindowManager *wm = CTX_wm_manager(C);
214 int tot = 0;
215
216 BLI_addtail(&wm->operators, op);
217
218 /* Only count registered operators. */
219 while (op) {
220 wmOperator *op_prev = op->prev;
221 if (op->type->flag & OPTYPE_REGISTER) {
222 tot += 1;
223 }
224 if (tot > MAX_OP_REGISTERED) {
225 BLI_remlink(&wm->operators, op);
226 WM_operator_free(op);
227 }
228 op = op_prev;
229 }
230
231 /* So the console is redrawn. */
232 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
233 WM_event_add_notifier(C, NC_WM | ND_HISTORY, NULL);
234 }
235
WM_operator_stack_clear(wmWindowManager * wm)236 void WM_operator_stack_clear(wmWindowManager *wm)
237 {
238 wmOperator *op;
239
240 while ((op = BLI_pophead(&wm->operators))) {
241 WM_operator_free(op);
242 }
243
244 WM_main_add_notifier(NC_WM | ND_HISTORY, NULL);
245 }
246
247 /**
248 * This function is needed in the case when an addon id disabled
249 * while a modal operator it defined is running.
250 */
WM_operator_handlers_clear(wmWindowManager * wm,wmOperatorType * ot)251 void WM_operator_handlers_clear(wmWindowManager *wm, wmOperatorType *ot)
252 {
253 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
254 ListBase *lb[2] = {&win->handlers, &win->modalhandlers};
255 for (int i = 0; i < ARRAY_SIZE(lb); i++) {
256 LISTBASE_FOREACH (wmEventHandler *, handler_base, lb[i]) {
257 if (handler_base->type == WM_HANDLER_TYPE_OP) {
258 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
259 if (handler->op && handler->op->type == ot) {
260 /* don't run op->cancel because it needs the context,
261 * assume whoever unregisters the operator will cleanup */
262 handler->head.flag |= WM_HANDLER_DO_FREE;
263 WM_operator_free(handler->op);
264 handler->op = NULL;
265 }
266 }
267 }
268 }
269 }
270 }
271
272 /* ****************************************** */
273
WM_keyconfig_reload(bContext * C)274 void WM_keyconfig_reload(bContext *C)
275 {
276 if (CTX_py_init_get(C) && !G.background) {
277 #ifdef WITH_PYTHON
278 BPY_run_string_eval(C, (const char *[]){"bpy", NULL}, "bpy.utils.keyconfig_init()");
279 #endif
280 }
281 }
282
WM_keyconfig_init(bContext * C)283 void WM_keyconfig_init(bContext *C)
284 {
285 wmWindowManager *wm = CTX_wm_manager(C);
286
287 /* Create standard key configs. */
288 if (wm->defaultconf == NULL) {
289 /* Keep lowercase to match the preset filename. */
290 wm->defaultconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT, false);
291 }
292 if (wm->addonconf == NULL) {
293 wm->addonconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT " addon", false);
294 }
295 if (wm->userconf == NULL) {
296 wm->userconf = WM_keyconfig_new(wm, WM_KEYCONFIG_STR_DEFAULT " user", false);
297 }
298
299 /* Initialize only after python init is done, for keymaps that use python operators. */
300 if (CTX_py_init_get(C) && (wm->initialized & WM_KEYCONFIG_IS_INIT) == 0) {
301 /* create default key config, only initialize once,
302 * it's persistent across sessions */
303 if (!(wm->defaultconf->flag & KEYCONF_INIT_DEFAULT)) {
304 wm_window_keymap(wm->defaultconf);
305 ED_spacetypes_keymap(wm->defaultconf);
306
307 WM_keyconfig_reload(C);
308
309 wm->defaultconf->flag |= KEYCONF_INIT_DEFAULT;
310 }
311
312 WM_keyconfig_update_tag(NULL, NULL);
313 WM_keyconfig_update(wm);
314
315 wm->initialized |= WM_KEYCONFIG_IS_INIT;
316 }
317 }
318
WM_check(bContext * C)319 void WM_check(bContext *C)
320 {
321 Main *bmain = CTX_data_main(C);
322 wmWindowManager *wm = CTX_wm_manager(C);
323
324 /* WM context. */
325 if (wm == NULL) {
326 wm = bmain->wm.first;
327 CTX_wm_manager_set(C, wm);
328 }
329
330 if (wm == NULL || BLI_listbase_is_empty(&wm->windows)) {
331 return;
332 }
333
334 /* Run before loading the keyconfig. */
335 if (wm->message_bus == NULL) {
336 wm->message_bus = WM_msgbus_create();
337 }
338
339 if (!G.background) {
340 /* Case: fileread. */
341 if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) {
342 WM_keyconfig_init(C);
343 WM_autosave_init(wm);
344 }
345
346 /* Case: no open windows at all, for old file reads. */
347 wm_window_ghostwindows_ensure(wm);
348 }
349
350 /* Case: fileread. */
351 /* Note: this runs in background mode to set the screen context cb. */
352 if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) {
353 ED_screens_init(bmain, wm);
354 wm->initialized |= WM_WINDOW_IS_INIT;
355 }
356 }
357
wm_clear_default_size(bContext * C)358 void wm_clear_default_size(bContext *C)
359 {
360 wmWindowManager *wm = CTX_wm_manager(C);
361
362 /* WM context. */
363 if (wm == NULL) {
364 wm = CTX_data_main(C)->wm.first;
365 CTX_wm_manager_set(C, wm);
366 }
367
368 if (wm == NULL || BLI_listbase_is_empty(&wm->windows)) {
369 return;
370 }
371
372 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
373 win->sizex = 0;
374 win->sizey = 0;
375 win->posx = 0;
376 win->posy = 0;
377 }
378 }
379
380 /* On startup, it adds all data, for matching. */
wm_add_default(Main * bmain,bContext * C)381 void wm_add_default(Main *bmain, bContext *C)
382 {
383 wmWindowManager *wm = BKE_libblock_alloc(bmain, ID_WM, "WinMan", 0);
384 wmWindow *win;
385 bScreen *screen = CTX_wm_screen(C); /* XXX from file read hrmf */
386 WorkSpace *workspace;
387 WorkSpaceLayout *layout = BKE_workspace_layout_find_global(bmain, screen, &workspace);
388
389 CTX_wm_manager_set(C, wm);
390 win = wm_window_new(bmain, wm, NULL, false);
391 win->scene = CTX_data_scene(C);
392 STRNCPY(win->view_layer_name, CTX_data_view_layer(C)->name);
393 BKE_workspace_active_set(win->workspace_hook, workspace);
394 BKE_workspace_active_layout_set(win->workspace_hook, win->winid, workspace, layout);
395 screen->winid = win->winid;
396
397 wm->winactive = win;
398 wm->file_saved = 1;
399 wm_window_make_drawable(wm, win);
400 }
401
402 /* Context is allowed to be NULL, do not free wm itself (lib_id.c). */
wm_close_and_free(bContext * C,wmWindowManager * wm)403 void wm_close_and_free(bContext *C, wmWindowManager *wm)
404 {
405 if (wm->autosavetimer) {
406 wm_autosave_timer_ended(wm);
407 }
408
409 #ifdef WITH_XR_OPENXR
410 /* May send notifier, so do before freeing notifier queue. */
411 wm_xr_exit(wm);
412 #endif
413
414 wmWindow *win;
415 while ((win = BLI_pophead(&wm->windows))) {
416 /* Prevent draw clear to use screen. */
417 BKE_workspace_active_set(win->workspace_hook, NULL);
418 wm_window_free(C, wm, win);
419 }
420
421 wmOperator *op;
422 while ((op = BLI_pophead(&wm->operators))) {
423 WM_operator_free(op);
424 }
425
426 wmKeyConfig *keyconf;
427 while ((keyconf = BLI_pophead(&wm->keyconfigs))) {
428 WM_keyconfig_free(keyconf);
429 }
430
431 BLI_freelistN(&wm->queue);
432
433 if (wm->message_bus != NULL) {
434 WM_msgbus_destroy(wm->message_bus);
435 }
436
437 BLI_freelistN(&wm->paintcursors);
438
439 WM_drag_free_list(&wm->drags);
440
441 wm_reports_free(wm);
442
443 if (wm->undo_stack) {
444 BKE_undosys_stack_destroy(wm->undo_stack);
445 wm->undo_stack = NULL;
446 }
447
448 if (C && CTX_wm_manager(C) == wm) {
449 CTX_wm_manager_set(C, NULL);
450 }
451 }
452
wm_close_and_free_all(bContext * C,ListBase * wmlist)453 void wm_close_and_free_all(bContext *C, ListBase *wmlist)
454 {
455 wmWindowManager *wm;
456 while ((wm = wmlist->first)) {
457 wm_close_and_free(C, wm);
458 BLI_remlink(wmlist, wm);
459 BKE_libblock_free_data(&wm->id, true);
460 MEM_freeN(wm);
461 }
462 }
463
WM_main(bContext * C)464 void WM_main(bContext *C)
465 {
466 /* Single refresh before handling events.
467 * This ensures we don't run operators before the depsgraph has been evaluated. */
468 wm_event_do_refresh_wm_and_depsgraph(C);
469
470 while (1) {
471
472 /* Get events from ghost, handle window events, add to window queues. */
473 wm_window_process_events(C);
474
475 /* Per window, all events to the window, screen, area and region handlers. */
476 wm_event_do_handlers(C);
477
478 /* Wvents have left notes about changes, we handle and cache it. */
479 wm_event_do_notifiers(C);
480
481 /* Wxecute cached changes draw. */
482 wm_draw_update(C);
483 }
484 }
485