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