Cleanup: add doxy sections for event queue handling
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
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  * Handle events and notifiers from GHOST input (mouse, keyboard, tablet, ndof).
24  *
25  * Also some operator reports utility functions.
26  */
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "DNA_listBase.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_userdef_types.h"
35 #include "DNA_windowmanager_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "CLG_log.h"
40
41 #include "GHOST_C-api.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_dynstr.h"
45 #include "BLI_math.h"
46 #include "BLI_timer.h"
47 #include "BLI_utildefines.h"
48
49 #include "BKE_context.h"
50 #include "BKE_customdata.h"
51 #include "BKE_global.h"
52 #include "BKE_idprop.h"
53 #include "BKE_main.h"
54 #include "BKE_report.h"
55 #include "BKE_scene.h"
56 #include "BKE_screen.h"
57 #include "BKE_workspace.h"
58
59 #include "BKE_sound.h"
60
61 #include "BLT_translation.h"
62
63 #include "ED_fileselect.h"
64 #include "ED_info.h"
65 #include "ED_screen.h"
66 #include "ED_undo.h"
67 #include "ED_util.h"
68 #include "ED_view3d.h"
69
70 #include "RNA_access.h"
71
72 #include "UI_interface.h"
73
74 #include "PIL_time.h"
75
76 #include "WM_api.h"
77 #include "WM_message.h"
78 #include "WM_toolsystem.h"
79 #include "WM_types.h"
80
81 #include "wm.h"
82 #include "wm_event_system.h"
83 #include "wm_event_types.h"
84 #include "wm_window.h"
85
86 #include "DEG_depsgraph.h"
87 #include "DEG_depsgraph_query.h"
88
89 /**
90  * When a gizmo is highlighted and uses click/drag events,
91  * this prevents mouse button press events from being passed through to other key-maps
92  * which would obscure those events.
93  *
94  * This allows gizmos that only use drag to co-exist with tools that use click.
95  *
96  * Without tools using press events which would prevent click/drag events getting to the gizmos.
97  *
98  * This is not a fool proof solution since since it's possible the gizmo operators would pass
99  * through these events when called, see: T65479.
100  */
101 #define USE_GIZMO_MOUSE_PRIORITY_HACK
102
103 static void wm_notifier_clear(wmNotifier *note);
104
105 static int wm_operator_call_internal(bContext *C,
106                                      wmOperatorType *ot,
107                                      PointerRNA *properties,
108                                      ReportList *reports,
109                                      const short context,
110                                      const bool poll_only,
111                                      wmEvent *event);
112
113 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot);
114
115 /* -------------------------------------------------------------------- */
116 /** \name Event Management
117  * \{ */
118
119 wmEvent *wm_event_add_ex(wmWindow *win,
120                          const wmEvent *event_to_add,
121                          const wmEvent *event_to_add_after)
122 {
123   wmEvent *event = MEM_mallocN(sizeof(wmEvent), "wmEvent");
124
125   *event = *event_to_add;
126
127   if (event_to_add_after == NULL) {
128     BLI_addtail(&win->queue, event);
129   }
130   else {
131     /* Note: strictly speaking this breaks const-correctness,
132      * however we're only changing 'next' member. */
133     BLI_insertlinkafter(&win->queue, (void *)event_to_add_after, event);
134   }
135   return event;
136 }
137
138 wmEvent *wm_event_add(wmWindow *win, const wmEvent *event_to_add)
139 {
140   return wm_event_add_ex(win, event_to_add, NULL);
141 }
142
143 wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
144 {
145   if ((G.f & G_FLAG_EVENT_SIMULATE) == 0) {
146     BLI_assert(0);
147     return NULL;
148   }
149   wmEvent *event = wm_event_add(win, event_to_add);
150   win->eventstate->x = event->x;
151   win->eventstate->y = event->y;
152   return event;
153 }
154
155 void wm_event_free(wmEvent *event)
156 {
157   if (event->customdata) {
158     if (event->customdatafree) {
159       /* note: pointer to listbase struct elsewhere */
160       if (event->custom == EVT_DATA_DRAGDROP) {
161         ListBase *lb = event->customdata;
162         WM_drag_free_list(lb);
163       }
164       else {
165         MEM_freeN(event->customdata);
166       }
167     }
168   }
169
170   MEM_freeN(event);
171 }
172
173 void wm_event_free_all(wmWindow *win)
174 {
175   wmEvent *event;
176
177   while ((event = BLI_pophead(&win->queue))) {
178     wm_event_free(event);
179   }
180 }
181
182 void wm_event_init_from_window(wmWindow *win, wmEvent *event)
183 {
184   *event = *(win->eventstate);
185 }
186
187 /** \} */
188
189 /* -------------------------------------------------------------------- */
190 /** \name Notifiers & Listeners
191  * \{ */
192
193 static bool wm_test_duplicate_notifier(const wmWindowManager *wm, uint type, void *reference)
194 {
195   LISTBASE_FOREACH (wmNotifier *, note, &wm->queue) {
196     if ((note->category | note->data | note->subtype | note->action) == type &&
197         note->reference == reference) {
198       return 1;
199     }
200   }
201
202   return 0;
203 }
204
205 void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
206 {
207   wmNotifier *note;
208
209   if (wm_test_duplicate_notifier(wm, type, reference)) {
210     return;
211   }
212
213   note = MEM_callocN(sizeof(wmNotifier), "notifier");
214
215   BLI_addtail(&wm->queue, note);
216
217   note->window = win;
218
219   note->category = type & NOTE_CATEGORY;
220   note->data = type & NOTE_DATA;
221   note->subtype = type & NOTE_SUBTYPE;
222   note->action = type & NOTE_ACTION;
223
224   note->reference = reference;
225 }
226
227 /* XXX: in future, which notifiers to send to other windows? */
228 void WM_event_add_notifier(const bContext *C, uint type, void *reference)
229 {
230   WM_event_add_notifier_ex(CTX_wm_manager(C), CTX_wm_window(C), type, reference);
231 }
232
233 void WM_main_add_notifier(unsigned int type, void *reference)
234 {
235   Main *bmain = G_MAIN;
236   wmWindowManager *wm = bmain->wm.first;
237   wmNotifier *note;
238
239   if (!wm || wm_test_duplicate_notifier(wm, type, reference)) {
240     return;
241   }
242
243   note = MEM_callocN(sizeof(wmNotifier), "notifier");
244
245   BLI_addtail(&wm->queue, note);
246
247   note->category = type & NOTE_CATEGORY;
248   note->data = type & NOTE_DATA;
249   note->subtype = type & NOTE_SUBTYPE;
250   note->action = type & NOTE_ACTION;
251
252   note->reference = reference;
253 }
254
255 /**
256  * Clear notifiers by reference, Used so listeners don't act on freed data.
257  */
258 void WM_main_remove_notifier_reference(const void *reference)
259 {
260   Main *bmain = G_MAIN;
261   wmWindowManager *wm = bmain->wm.first;
262
263   if (wm) {
264     wmNotifier *note, *note_next;
265
266     for (note = wm->queue.first; note; note = note_next) {
267       note_next = note->next;
268
269       if (note->reference == reference) {
270         /* don't remove because this causes problems for #wm_event_do_notifiers
271          * which may be looping on the data (deleting screens) */
272         wm_notifier_clear(note);
273       }
274     }
275
276     /* Remap instead. */
277 #if 0
278     if (wm->message_bus) {
279       WM_msg_id_remove(wm->message_bus, reference);
280     }
281 #endif
282   }
283 }
284
285 void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
286 {
287   Main *bmain = G_MAIN;
288   bScreen *screen;
289
290   for (screen = bmain->screens.first; screen; screen = screen->id.next) {
291     ScrArea *area;
292
293     for (area = screen->areabase.first; area; area = area->next) {
294       SpaceLink *sl;
295
296       for (sl = area->spacedata.first; sl; sl = sl->next) {
297         ED_spacedata_id_remap(area, sl, old_id, new_id);
298       }
299     }
300   }
301
302   wmWindowManager *wm = bmain->wm.first;
303   if (wm && wm->message_bus) {
304     struct wmMsgBus *mbus = wm->message_bus;
305     if (new_id != NULL) {
306       WM_msg_id_update(mbus, old_id, new_id);
307     }
308     else {
309       WM_msg_id_remove(mbus, old_id);
310     }
311   }
312 }
313
314 static void wm_notifier_clear(wmNotifier *note)
315 {
316   /* NULL the entire notifier, only leaving (next, prev) members intact */
317   memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
318 }
319
320 void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
321 {
322   wmWindowManager *wm = CTX_wm_manager(C);
323   /* The whole idea of locked interface is to prevent viewport and whatever
324    * thread to modify the same data. Because of this, we can not perform
325    * dependency graph update.
326    */
327   if (wm->is_interface_locked) {
328     return;
329   }
330   /* Combine datamasks so 1 win doesn't disable UV's in another [#26448]. */
331   CustomData_MeshMasks win_combine_v3d_datamask = {0};
332   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
333     const Scene *scene = WM_window_get_active_scene(win);
334     const bScreen *screen = WM_window_get_active_screen(win);
335
336     ED_view3d_screen_datamask(C, scene, screen, &win_combine_v3d_datamask);
337   }
338   /* Update all the dependency graphs of visible view layers. */
339   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
340     Scene *scene = WM_window_get_active_scene(win);
341     ViewLayer *view_layer = WM_window_get_active_view_layer(win);
342     Main *bmain = CTX_data_main(C);
343     /* Copied to set's in scene_update_tagged_recursive() */
344     scene->customdata_mask = win_combine_v3d_datamask;
345     /* XXX, hack so operators can enforce datamasks [#26482], gl render */
346     CustomData_MeshMasks_update(&scene->customdata_mask, &scene->customdata_mask_modal);
347     /* TODO(sergey): For now all dependency graphs which are evaluated from
348      * workspace are considered active. This will work all fine with "locked"
349      * view layer and time across windows. This is to be granted separately,
350      * and for until then we have to accept ambiguities when object is shared
351      * across visible view layers and has overrides on it.
352      */
353     Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true);
354     if (is_after_open_file) {
355       DEG_graph_relations_update(depsgraph, bmain, scene, view_layer);
356       DEG_graph_on_visible_update(bmain, depsgraph, true);
357     }
358     DEG_make_active(depsgraph);
359     BKE_scene_graph_update_tagged(depsgraph, bmain);
360   }
361 }
362
363 /**
364  * Was part of #wm_event_do_notifiers,
365  * split out so it can be called once before entering the #WM_main loop.
366  * This ensures operators don't run before the UI and depsgraph are initialized.
367  */
368 void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
369 {
370   wmWindowManager *wm = CTX_wm_manager(C);
371   /* cached: editor refresh callbacks now, they get context */
372   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
373     const bScreen *screen = WM_window_get_active_screen(win);
374     ScrArea *area;
375
376     CTX_wm_window_set(C, win);
377     for (area = screen->areabase.first; area; area = area->next) {
378       if (area->do_refresh) {
379         CTX_wm_area_set(C, area);
380         ED_area_do_refresh(C, area);
381       }
382     }
383   }
384
385   wm_event_do_depsgraph(C, false);
386
387   CTX_wm_window_set(C, NULL);
388 }
389
390 /* called in mainloop */
391 void wm_event_do_notifiers(bContext *C)
392 {
393   wmWindowManager *wm = CTX_wm_manager(C);
394   wmNotifier *note, *next;
395   wmWindow *win;
396
397   if (wm == NULL) {
398     return;
399   }
400
401   BLI_timer_execute();
402
403   /* disable? - keep for now since its used for window level notifiers. */
404 #if 1
405   /* cache & catch WM level notifiers, such as frame change, scene/screen set */
406   for (win = wm->windows.first; win; win = win->next) {
407     Scene *scene = WM_window_get_active_scene(win);
408     bool do_anim = false;
409
410     CTX_wm_window_set(C, win);
411
412     for (note = wm->queue.first; note; note = next) {
413       next = note->next;
414
415       if (note->category == NC_WM) {
416         if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
417           wm->file_saved = 1;
418           wm_window_title(wm, win);
419         }
420         else if (note->data == ND_DATACHANGED) {
421           wm_window_title(wm, win);
422         }
423       }
424       if (note->window == win) {
425         if (note->category == NC_SCREEN) {
426           if (note->data == ND_WORKSPACE_SET) {
427             WorkSpace *ref_ws = note->reference;
428
429             UI_popup_handlers_remove_all(C, &win->modalhandlers);
430
431             WM_window_set_active_workspace(C, win, ref_ws);
432             if (G.debug & G_DEBUG_EVENTS) {
433               printf("%s: Workspace set %p\n", __func__, note->reference);
434             }
435           }
436           else if (note->data == ND_WORKSPACE_DELETE) {
437             WorkSpace *workspace = note->reference;
438
439             ED_workspace_delete(workspace, CTX_data_main(C), C, wm);  // XXX hrms, think this over!
440             if (G.debug & G_DEBUG_EVENTS) {
441               printf("%s: Workspace delete %p\n", __func__, workspace);
442             }
443           }
444           else if (note->data == ND_LAYOUTBROWSE) {
445             bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
446
447             /* free popup handlers only [#35434] */
448             UI_popup_handlers_remove_all(C, &win->modalhandlers);
449
450             ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
451             if (G.debug & G_DEBUG_EVENTS) {
452               printf("%s: screen set %p\n", __func__, note->reference);
453             }
454           }
455           else if (note->data == ND_LAYOUTDELETE) {
456             WorkSpace *workspace = WM_window_get_active_workspace(win);
457             WorkSpaceLayout *layout = note->reference;
458
459             ED_workspace_layout_delete(workspace, layout, C);  // XXX hrms, think this over!
460             if (G.debug & G_DEBUG_EVENTS) {
461               printf("%s: screen delete %p\n", __func__, note->reference);
462             }
463           }
464         }
465       }
466
467       if (note->window == win ||
468           (note->window == NULL && (note->reference == NULL || note->reference == scene))) {
469         if (note->category == NC_SCENE) {
470           if (note->data == ND_FRAME) {
471             do_anim = true;
472           }
473         }
474       }
475       if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
476         ViewLayer *view_layer = CTX_data_view_layer(C);
477         ED_info_stats_clear(view_layer);
478         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
479       }
480     }
481     if (do_anim) {
482
483       /* XXX, quick frame changes can cause a crash if framechange and rendering
484        * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
485        * twice which can depgraph update the same object at once */
486       if (G.is_rendering == false) {
487         /* depsgraph gets called, might send more notifiers */
488         Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
489         ED_update_for_newframe(CTX_data_main(C), depsgraph);
490       }
491     }
492   }
493
494   /* the notifiers are sent without context, to keep it clean */
495   while ((note = BLI_pophead(&wm->queue))) {
496     for (win = wm->windows.first; win; win = win->next) {
497       Scene *scene = WM_window_get_active_scene(win);
498       bScreen *screen = WM_window_get_active_screen(win);
499       WorkSpace *workspace = WM_window_get_active_workspace(win);
500
501       /* filter out notifiers */
502       if (note->category == NC_SCREEN && note->reference && note->reference != screen &&
503           note->reference != workspace && note->reference != WM_window_get_active_layout(win)) {
504         /* pass */
505       }
506       else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
507         /* pass */
508       }
509       else {
510         ARegion *region;
511
512         /* XXX context in notifiers? */
513         CTX_wm_window_set(C, win);
514
515 #  if 0
516         printf("notifier win %d screen %s cat %x\n",
517                win->winid,
518                win->screen->id.name + 2,
519                note->category);
520 #  endif
521         ED_screen_do_listen(C, note);
522
523         for (region = screen->regionbase.first; region; region = region->next) {
524           ED_region_do_listen(win, NULL, region, note, scene);
525         }
526
527         ED_screen_areas_iter (win, screen, area) {
528           ED_area_do_listen(win, area, note, scene);
529           for (region = area->regionbase.first; region; region = region->next) {
530             ED_region_do_listen(win, area, region, note, scene);
531           }
532         }
533       }
534     }
535
536     MEM_freeN(note);
537   }
538 #endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */
539
540   /* Handle message bus. */
541   {
542     for (win = wm->windows.first; win; win = win->next) {
543       CTX_wm_window_set(C, win);
544       WM_msgbus_handle(wm->message_bus, C);
545     }
546     CTX_wm_window_set(C, NULL);
547   }
548
549   wm_event_do_refresh_wm_and_depsgraph(C);
550
551   /* Status bar */
552   if (wm->winactive) {
553     win = wm->winactive;
554     CTX_wm_window_set(C, win);
555     WM_window_cursor_keymap_status_refresh(C, win);
556     CTX_wm_window_set(C, NULL);
557   }
558
559   /* Autorun warning */
560   wm_test_autorun_warning(C);
561 }
562
563 static int wm_event_always_pass(const wmEvent *event)
564 {
565   /* some events we always pass on, to ensure proper communication */
566   return ISTIMER(event->type) || (event->type == WINDEACTIVATE) || (event->type == EVT_FILESELECT);
567 }
568
569 /** \} */
570
571 /* -------------------------------------------------------------------- */
572 /** \name UI Handling
573  * \{ */
574
575 static int wm_handler_ui_call(bContext *C,
576                               wmEventHandler_UI *handler,
577                               const wmEvent *event,
578                               int always_pass)
579 {
580   ScrArea *area = CTX_wm_area(C);
581   ARegion *region = CTX_wm_region(C);
582   ARegion *menu = CTX_wm_menu(C);
583   static bool do_wheel_ui = true;
584   const bool is_wheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN);
585   int retval;
586
587   /* UI code doesn't handle return values - it just always returns break.
588    * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */
589   if (((handler->head.flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) && !ISMOUSE_BUTTON(event->type) &&
590       (event->val == KM_DBL_CLICK)) {
591     return WM_HANDLER_CONTINUE;
592   }
593
594   /* UI is quite aggressive with swallowing events, like scrollwheel */
595   /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
596   if (do_wheel_ui == false) {
597     if (is_wheel) {
598       return WM_HANDLER_CONTINUE;
599     }
600     else if (wm_event_always_pass(event) == 0) {
601       do_wheel_ui = true;
602     }
603   }
604
605   /* we set context to where ui handler came from */
606   if (handler->context.area) {
607     CTX_wm_area_set(C, handler->context.area);
608   }
609   if (handler->context.region) {
610     CTX_wm_region_set(C, handler->context.region);
611   }
612   if (handler->context.menu) {
613     CTX_wm_menu_set(C, handler->context.menu);
614   }
615
616   retval = handler->handle_fn(C, event, handler->user_data);
617
618   /* putting back screen context */
619   if ((retval != WM_UI_HANDLER_BREAK) || always_pass) {
620     CTX_wm_area_set(C, area);
621     CTX_wm_region_set(C, region);
622     CTX_wm_menu_set(C, menu);
623   }
624   else {
625     /* this special cases is for areas and regions that get removed */
626     CTX_wm_area_set(C, NULL);
627     CTX_wm_region_set(C, NULL);
628     CTX_wm_menu_set(C, NULL);
629   }
630
631   if (retval == WM_UI_HANDLER_BREAK) {
632     return WM_HANDLER_BREAK;
633   }
634
635   /* event not handled in UI, if wheel then we temporarily disable it */
636   if (is_wheel) {
637     do_wheel_ui = false;
638   }
639
640   return WM_HANDLER_CONTINUE;
641 }
642
643 void wm_event_handler_ui_cancel_ex(bContext *C,
644                                    wmWindow *win,
645                                    ARegion *region,
646                                    bool reactivate_button)
647 {
648   if (!region) {
649     return;
650   }
651
652   LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &region->handlers) {
653     if (handler_base->type == WM_HANDLER_TYPE_UI) {
654       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
655       BLI_assert(handler->handle_fn != NULL);
656       wmEvent event;
657       wm_event_init_from_window(win, &event);
658       event.type = EVT_BUT_CANCEL;
659       event.val = reactivate_button ? 0 : 1;
660       handler->handle_fn(C, &event, handler->user_data);
661     }
662   }
663 }
664
665 static void wm_event_handler_ui_cancel(bContext *C)
666 {
667   wmWindow *win = CTX_wm_window(C);
668   ARegion *region = CTX_wm_region(C);
669   wm_event_handler_ui_cancel_ex(C, win, region, true);
670 }
671
672 /** \} */
673
674 /* -------------------------------------------------------------------- */
675 /** \name WM Reports
676  *
677  * Access to #wmWindowManager.reports
678  * \{ */
679
680 /**
681  * Show the report in the info header.
682  */
683 void WM_report_banner_show(void)
684 {
685   wmWindowManager *wm = G_MAIN->wm.first;
686   ReportList *wm_reports = &wm->reports;
687   ReportTimerInfo *rti;
688
689   /* After adding reports to the global list, reset the report timer. */
690   WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
691
692   /* Records time since last report was added */
693   wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05);
694
695   rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
696   wm_reports->reporttimer->customdata = rti;
697 }
698
699 /**
700  * Hide all currently displayed banners and abort their timer.
701  */
702 void WM_report_banners_cancel(Main *bmain)
703 {
704   wmWindowManager *wm = bmain->wm.first;
705   BKE_reports_clear(&wm->reports);
706   WM_event_remove_timer(wm, NULL, wm->reports.reporttimer);
707 }
708
709 #ifdef WITH_INPUT_NDOF
710 void WM_ndof_deadzone_set(float deadzone)
711 {
712   GHOST_setNDOFDeadZone(deadzone);
713 }
714 #endif
715
716 static void wm_add_reports(ReportList *reports)
717 {
718   /* if the caller owns them, handle this */
719   if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) {
720     wmWindowManager *wm = G_MAIN->wm.first;
721
722     /* add reports to the global list, otherwise they are not seen */
723     BLI_movelisttolist(&wm->reports.list, &reports->list);
724
725     WM_report_banner_show();
726   }
727 }
728
729 void WM_report(ReportType type, const char *message)
730 {
731   ReportList reports;
732
733   BKE_reports_init(&reports, RPT_STORE);
734   BKE_report(&reports, type, message);
735
736   wm_add_reports(&reports);
737
738   BKE_reports_clear(&reports);
739 }
740
741 void WM_reportf(ReportType type, const char *format, ...)
742 {
743   DynStr *ds;
744   va_list args;
745
746   ds = BLI_dynstr_new();
747   va_start(args, format);
748   BLI_dynstr_vappendf(ds, format, args);
749   va_end(args);
750
751   char *str = BLI_dynstr_get_cstring(ds);
752   WM_report(type, str);
753   MEM_freeN(str);
754
755   BLI_dynstr_free(ds);
756 }
757
758 /** \} */
759
760 /* -------------------------------------------------------------------- */
761 /** \name Operator Logic
762  * \{ */
763
764 bool WM_operator_poll(bContext *C, wmOperatorType *ot)
765 {
766   wmOperatorTypeMacro *otmacro;
767
768   for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
769     wmOperatorType *ot_macro = WM_operatortype_find(otmacro->idname, 0);
770
771     if (0 == WM_operator_poll(C, ot_macro)) {
772       return 0;
773     }
774   }
775
776   /* python needs operator type, so we added exception for it */
777   if (ot->pyop_poll) {
778     return ot->pyop_poll(C, ot);
779   }
780   else if (ot->poll) {
781     return ot->poll(C);
782   }
783
784   return 1;
785 }
786
787 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
788 bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
789 {
790   return wm_operator_call_internal(C, ot, NULL, NULL, context, true, NULL);
791 }
792
793 bool WM_operator_check_ui_empty(wmOperatorType *ot)
794 {
795   if (ot->macro.first != NULL) {
796     /* for macros, check all have exec() we can call */
797     wmOperatorTypeMacro *otmacro;
798     for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
799       wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
800       if (otm && !WM_operator_check_ui_empty(otm)) {
801         return false;
802       }
803     }
804     return true;
805   }
806
807   /* Assume a ui callback will draw something. */
808   if (ot->ui) {
809     return false;
810   }
811
812   PointerRNA ptr;
813   WM_operator_properties_create_ptr(&ptr, ot);
814   RNA_STRUCT_BEGIN (&ptr, prop) {
815     int flag = RNA_property_flag(prop);
816     if (flag & PROP_HIDDEN) {
817       continue;
818     }
819     return false;
820   }
821   RNA_STRUCT_END;
822   return true;
823 }
824
825 /**
826  * Sets the active region for this space from the context.
827  *
828  * \see #BKE_area_find_region_active_win
829  */
830 void WM_operator_region_active_win_set(bContext *C)
831 {
832   ScrArea *area = CTX_wm_area(C);
833   if (area) {
834     ARegion *region = CTX_wm_region(C);
835     if (region && region->regiontype == RGN_TYPE_WINDOW) {
836       area->region_active_win = BLI_findindex(&area->regionbase, region);
837     }
838   }
839 }
840
841 /* (caller_owns_reports == true) when called from python */
842 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports)
843 {
844   if (G.background == 0 && caller_owns_reports == false) { /* popup */
845     if (op->reports->list.first) {
846       /* FIXME, temp setting window, see other call to UI_popup_menu_reports for why */
847       wmWindow *win_prev = CTX_wm_window(C);
848       ScrArea *area_prev = CTX_wm_area(C);
849       ARegion *region_prev = CTX_wm_region(C);
850
851       if (win_prev == NULL) {
852         CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
853       }
854
855       UI_popup_menu_reports(C, op->reports);
856
857       CTX_wm_window_set(C, win_prev);
858       CTX_wm_area_set(C, area_prev);
859       CTX_wm_region_set(C, region_prev);
860     }
861   }
862
863   if (retval & OPERATOR_FINISHED) {
864     CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
865
866     if (caller_owns_reports == false) {
867       BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
868     }
869
870     if (op->type->flag & OPTYPE_REGISTER) {
871       if (G.background == 0) { /* ends up printing these in the terminal, gets annoying */
872         /* Report the python string representation of the operator */
873         char *buf = WM_operator_pystring(C, op, false, true);
874         BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
875         MEM_freeN(buf);
876       }
877     }
878   }
879
880   /* Refresh Info Editor with reports immediately, even if op returned OPERATOR_CANCELLED. */
881   if ((retval & OPERATOR_CANCELLED) && !BLI_listbase_is_empty(&op->reports->list)) {
882     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
883   }
884   /* if the caller owns them, handle this */
885   wm_add_reports(op->reports);
886 }
887
888 /**
889  * This function is mainly to check that the rules for freeing
890  * an operator are kept in sync.
891  */
892 static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
893 {
894   /* Check undo flag here since undo operators are also added to the list,
895    * to support checking if the same operator is run twice. */
896   return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
897 }
898
899 static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
900 {
901   wmWindowManager *wm = CTX_wm_manager(C);
902   enum {
903     NOP,
904     SET,
905     CLEAR,
906   } hud_status = NOP;
907
908   op->customdata = NULL;
909
910   if (store) {
911     WM_operator_last_properties_store(op);
912   }
913
914   /* we don't want to do undo pushes for operators that are being
915    * called from operators that already do an undo push. usually
916    * this will happen for python operators that call C operators */
917   if (wm->op_undo_depth == 0) {
918     if (op->type->flag & OPTYPE_UNDO) {
919       ED_undo_push_op(C, op);
920       if (repeat == 0) {
921         hud_status = CLEAR;
922       }
923     }
924     else if (op->type->flag & OPTYPE_UNDO_GROUPED) {
925       ED_undo_grouped_push_op(C, op);
926       if (repeat == 0) {
927         hud_status = CLEAR;
928       }
929     }
930   }
931
932   if (repeat == 0) {
933     if (G.debug & G_DEBUG_WM) {
934       char *buf = WM_operator_pystring(C, op, false, true);
935       BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
936       MEM_freeN(buf);
937     }
938
939     if (wm_operator_register_check(wm, op->type)) {
940       /* take ownership of reports (in case python provided own) */
941       op->reports->flag |= RPT_FREE;
942
943       wm_operator_register(C, op);
944       WM_operator_region_active_win_set(C);
945
946       if (WM_operator_last_redo(C) == op) {
947         /* Show the redo panel. */
948         hud_status = SET;
949       }
950     }
951     else {
952       WM_operator_free(op);
953     }
954   }
955
956   if (hud_status != NOP) {
957     if (hud_status == SET) {
958       ScrArea *area = CTX_wm_area(C);
959       if (area) {
960         ED_area_type_hud_ensure(C, area);
961       }
962     }
963     else if (hud_status == CLEAR) {
964       ED_area_type_hud_clear(wm, NULL);
965     }
966     else {
967       BLI_assert(0);
968     }
969   }
970 }
971
972 /* if repeat is true, it doesn't register again, nor does it free */
973 static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, const bool store)
974 {
975   wmWindowManager *wm = CTX_wm_manager(C);
976   int retval = OPERATOR_CANCELLED;
977
978   CTX_wm_operator_poll_msg_set(C, NULL);
979
980   if (op == NULL || op->type == NULL) {
981     return retval;
982   }
983
984   if (0 == WM_operator_poll(C, op->type)) {
985     return retval;
986   }
987
988   if (op->type->exec) {
989     if (op->type->flag & OPTYPE_UNDO) {
990       wm->op_undo_depth++;
991     }
992
993     retval = op->type->exec(C, op);
994     OPERATOR_RETVAL_CHECK(retval);
995
996     if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
997       wm->op_undo_depth--;
998     }
999   }
1000
1001   /* XXX(mont29) Disabled the repeat check to address part 2 of T31840.
1002    * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
1003    * why this was needed, but worth to note it in case something turns bad. */
1004   if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
1005     wm_operator_reports(C, op, retval, false);
1006   }
1007
1008   if (retval & OPERATOR_FINISHED) {
1009     wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
1010   }
1011   else if (repeat == 0) {
1012     /* warning: modal from exec is bad practice, but avoid crashing. */
1013     if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
1014       WM_operator_free(op);
1015     }
1016   }
1017
1018   return retval | OPERATOR_HANDLED;
1019 }
1020
1021 /* simply calls exec with basic checks */
1022 static int wm_operator_exec_notest(bContext *C, wmOperator *op)
1023 {
1024   int retval = OPERATOR_CANCELLED;
1025
1026   if (op == NULL || op->type == NULL || op->type->exec == NULL) {
1027     return retval;
1028   }
1029
1030   retval = op->type->exec(C, op);
1031   OPERATOR_RETVAL_CHECK(retval);
1032
1033   return retval;
1034 }
1035
1036 /**
1037  * for running operators with frozen context (modal handlers, menus)
1038  *
1039  * \param store: Store settings for re-use.
1040  *
1041  * warning: do not use this within an operator to call its self! [#29537] */
1042 int WM_operator_call_ex(bContext *C, wmOperator *op, const bool store)
1043 {
1044   return wm_operator_exec(C, op, false, store);
1045 }
1046
1047 int WM_operator_call(bContext *C, wmOperator *op)
1048 {
1049   return WM_operator_call_ex(C, op, false);
1050 }
1051
1052 /**
1053  * This is intended to be used when an invoke operator wants to call exec on its self
1054  * and is basically like running op->type->exec() directly, no poll checks no freeing,
1055  * since we assume whoever called invoke will take care of that
1056  */
1057 int WM_operator_call_notest(bContext *C, wmOperator *op)
1058 {
1059   return wm_operator_exec_notest(C, op);
1060 }
1061
1062 /**
1063  * Execute this operator again, put here so it can share above code
1064  */
1065 int WM_operator_repeat(bContext *C, wmOperator *op)
1066 {
1067   const int op_flag = OP_IS_REPEAT;
1068   op->flag |= op_flag;
1069   const int ret = wm_operator_exec(C, op, true, true);
1070   op->flag &= ~op_flag;
1071   return ret;
1072 }
1073 int WM_operator_repeat_last(bContext *C, wmOperator *op)
1074 {
1075   const int op_flag = OP_IS_REPEAT_LAST;
1076   op->flag |= op_flag;
1077   const int ret = wm_operator_exec(C, op, true, true);
1078   op->flag &= ~op_flag;
1079   return ret;
1080 }
1081 /**
1082  * \return true if #WM_operator_repeat can run
1083  * simple check for now but may become more involved.
1084  * To be sure the operator can run call `WM_operator_poll(C, op->type)` also, since this call
1085  * checks if #WM_operator_repeat() can run at all, not that it WILL run at any time.
1086  */
1087 bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
1088 {
1089   if (op->type->exec != NULL) {
1090     return true;
1091   }
1092   else if (op->opm) {
1093     /* for macros, check all have exec() we can call */
1094     wmOperatorTypeMacro *otmacro;
1095     for (otmacro = op->opm->type->macro.first; otmacro; otmacro = otmacro->next) {
1096       wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1097       if (otm && otm->exec == NULL) {
1098         return false;
1099       }
1100     }
1101     return true;
1102   }
1103
1104   return false;
1105 }
1106
1107 bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
1108 {
1109   /* may be in the operators list or not */
1110   wmOperator *op_prev;
1111   if (op->prev == NULL && op->next == NULL) {
1112     wmWindowManager *wm = CTX_wm_manager(C);
1113     op_prev = wm->operators.last;
1114   }
1115   else {
1116     op_prev = op->prev;
1117   }
1118   return (op_prev && (op->type == op_prev->type));
1119 }
1120
1121 static wmOperator *wm_operator_create(wmWindowManager *wm,
1122                                       wmOperatorType *ot,
1123                                       PointerRNA *properties,
1124                                       ReportList *reports)
1125 {
1126   /* XXX operatortype names are static still. for debug */
1127   wmOperator *op = MEM_callocN(sizeof(wmOperator), ot->idname);
1128
1129   /* XXX adding new operator could be function, only happens here now */
1130   op->type = ot;
1131   BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
1132
1133   /* initialize properties, either copy or create */
1134   op->ptr = MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
1135   if (properties && properties->data) {
1136     op->properties = IDP_CopyProperty(properties->data);
1137   }
1138   else {
1139     IDPropertyTemplate val = {0};
1140     op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1141   }
1142   RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
1143
1144   /* initialize error reports */
1145   if (reports) {
1146     op->reports = reports; /* must be initialized already */
1147   }
1148   else {
1149     op->reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
1150     BKE_reports_init(op->reports, RPT_STORE | RPT_FREE);
1151   }
1152
1153   /* recursive filling of operator macro list */
1154   if (ot->macro.first) {
1155     static wmOperator *motherop = NULL;
1156     wmOperatorTypeMacro *otmacro;
1157     int root = 0;
1158
1159     /* ensure all ops are in execution order in 1 list */
1160     if (motherop == NULL) {
1161       motherop = op;
1162       root = 1;
1163     }
1164
1165     /* if properties exist, it will contain everything needed */
1166     if (properties) {
1167       otmacro = ot->macro.first;
1168
1169       RNA_STRUCT_BEGIN (properties, prop) {
1170
1171         if (otmacro == NULL) {
1172           break;
1173         }
1174
1175         /* skip invalid properties */
1176         if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
1177           wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1178           PointerRNA someptr = RNA_property_pointer_get(properties, prop);
1179           wmOperator *opm = wm_operator_create(wm, otm, &someptr, NULL);
1180
1181           IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
1182
1183           BLI_addtail(&motherop->macro, opm);
1184           opm->opm = motherop; /* pointer to mom, for modal() */
1185
1186           otmacro = otmacro->next;
1187         }
1188       }
1189       RNA_STRUCT_END;
1190     }
1191     else {
1192       for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
1193         wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1194         wmOperator *opm = wm_operator_create(wm, otm, otmacro->ptr, NULL);
1195
1196         BLI_addtail(&motherop->macro, opm);
1197         opm->opm = motherop; /* pointer to mom, for modal() */
1198       }
1199     }
1200
1201     if (root) {
1202       motherop = NULL;
1203     }
1204   }
1205
1206   WM_operator_properties_sanitize(op->ptr, 0);
1207
1208   return op;
1209 }
1210
1211 static void wm_region_mouse_co(bContext *C, wmEvent *event)
1212 {
1213   ARegion *region = CTX_wm_region(C);
1214   if (region) {
1215     /* compatibility convention */
1216     event->mval[0] = event->x - region->winrct.xmin;
1217     event->mval[1] = event->y - region->winrct.ymin;
1218   }
1219   else {
1220     /* these values are invalid (avoid odd behavior by relying on old mval values) */
1221     event->mval[0] = -1;
1222     event->mval[1] = -1;
1223   }
1224 }
1225
1226 /**
1227  * Also used for exec when 'event' is NULL.
1228  */
1229 static int wm_operator_invoke(bContext *C,
1230                               wmOperatorType *ot,
1231                               wmEvent *event,
1232                               PointerRNA *properties,
1233                               ReportList *reports,
1234                               const bool poll_only,
1235                               bool use_last_properties)
1236 {
1237   int retval = OPERATOR_PASS_THROUGH;
1238
1239   /* This is done because complicated setup is done to call this function
1240    * that is better not duplicated. */
1241   if (poll_only) {
1242     return WM_operator_poll(C, ot);
1243   }
1244
1245   if (WM_operator_poll(C, ot)) {
1246     wmWindowManager *wm = CTX_wm_manager(C);
1247
1248     /* if reports == NULL, they'll be initialized */
1249     wmOperator *op = wm_operator_create(wm, ot, properties, reports);
1250
1251     const bool is_nested_call = (wm->op_undo_depth != 0);
1252
1253     if (event != NULL) {
1254       op->flag |= OP_IS_INVOKE;
1255     }
1256
1257     /* initialize setting from previous run */
1258     if (!is_nested_call && use_last_properties) { /* not called by py script */
1259       WM_operator_last_properties_init(op);
1260     }
1261
1262     if ((event == NULL) || (event->type != MOUSEMOVE)) {
1263       CLOG_INFO(WM_LOG_HANDLERS,
1264                 2,
1265                 "handle evt %d win %p op %s",
1266                 event ? event->type : 0,
1267                 CTX_wm_screen(C)->active_region,
1268                 ot->idname);
1269     }
1270
1271     if (op->type->invoke && event) {
1272       wm_region_mouse_co(C, event);
1273
1274       if (op->type->flag & OPTYPE_UNDO) {
1275         wm->op_undo_depth++;
1276       }
1277
1278       retval = op->type->invoke(C, op, event);
1279       OPERATOR_RETVAL_CHECK(retval);
1280
1281       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1282         wm->op_undo_depth--;
1283       }
1284     }
1285     else if (op->type->exec) {
1286       if (op->type->flag & OPTYPE_UNDO) {
1287         wm->op_undo_depth++;
1288       }
1289
1290       retval = op->type->exec(C, op);
1291       OPERATOR_RETVAL_CHECK(retval);
1292
1293       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1294         wm->op_undo_depth--;
1295       }
1296     }
1297     else {
1298       /* debug, important to leave a while, should never happen */
1299       CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
1300     }
1301
1302     /* Note, if the report is given as an argument then assume the caller will deal with displaying
1303      * them currently Python only uses this. */
1304     if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
1305       /* only show the report if the report list was not given in the function */
1306       wm_operator_reports(C, op, retval, (reports != NULL));
1307     }
1308
1309     if (retval & OPERATOR_HANDLED) {
1310       /* do nothing, wm_operator_exec() has been called somewhere */
1311     }
1312     else if (retval & OPERATOR_FINISHED) {
1313       const bool store = !is_nested_call && use_last_properties;
1314       wm_operator_finished(C, op, false, store);
1315     }
1316     else if (retval & OPERATOR_RUNNING_MODAL) {
1317       /* take ownership of reports (in case python provided own) */
1318       op->reports->flag |= RPT_FREE;
1319
1320       /* grab cursor during blocking modal ops (X11)
1321        * Also check for macro
1322        */
1323       if (ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
1324         int bounds[4] = {-1, -1, -1, -1};
1325         int wrap = WM_CURSOR_WRAP_NONE;
1326
1327         if (event && (U.uiflag & USER_CONTINUOUS_MOUSE)) {
1328           const wmOperator *op_test = op->opm ? op->opm : op;
1329           const wmOperatorType *ot_test = op_test->type;
1330           if ((ot_test->flag & OPTYPE_GRAB_CURSOR_XY) ||
1331               (op_test->flag & OP_IS_MODAL_GRAB_CURSOR)) {
1332             wrap = WM_CURSOR_WRAP_XY;
1333           }
1334           else if (ot_test->flag & OPTYPE_GRAB_CURSOR_X) {
1335             wrap = WM_CURSOR_WRAP_X;
1336           }
1337           else if (ot_test->flag & OPTYPE_GRAB_CURSOR_Y) {
1338             wrap = WM_CURSOR_WRAP_Y;
1339           }
1340         }
1341
1342         if (wrap) {
1343           const rcti *winrect = NULL;
1344           ARegion *region = CTX_wm_region(C);
1345           ScrArea *area = CTX_wm_area(C);
1346
1347           /* Wrap only in X for header. */
1348           if (region &&
1349               ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER, RGN_TYPE_FOOTER)) {
1350             wrap = WM_CURSOR_WRAP_X;
1351           }
1352
1353           if (region && region->regiontype == RGN_TYPE_WINDOW &&
1354               BLI_rcti_isect_pt_v(&region->winrct, &event->x)) {
1355             winrect = &region->winrct;
1356           }
1357           else if (area && BLI_rcti_isect_pt_v(&area->totrct, &event->x)) {
1358             winrect = &area->totrct;
1359           }
1360
1361           if (winrect) {
1362             bounds[0] = winrect->xmin;
1363             bounds[1] = winrect->ymax;
1364             bounds[2] = winrect->xmax;
1365             bounds[3] = winrect->ymin;
1366           }
1367         }
1368
1369         WM_cursor_grab_enable(CTX_wm_window(C), wrap, false, bounds);
1370       }
1371
1372       /* cancel UI handlers, typically tooltips that can hang around
1373        * while dragging the view or worse, that stay there permanently
1374        * after the modal operator has swallowed all events and passed
1375        * none to the UI handler */
1376       wm_event_handler_ui_cancel(C);
1377     }
1378     else {
1379       WM_operator_free(op);
1380     }
1381   }
1382
1383   return retval;
1384 }
1385
1386 /**
1387  * #WM_operator_name_call is the main accessor function
1388  * this is for python to access since its done the operator lookup
1389  *
1390  * invokes operator in context
1391  */
1392 static int wm_operator_call_internal(bContext *C,
1393                                      wmOperatorType *ot,
1394                                      PointerRNA *properties,
1395                                      ReportList *reports,
1396                                      const short context,
1397                                      const bool poll_only,
1398                                      wmEvent *event)
1399 {
1400   int retval;
1401
1402   CTX_wm_operator_poll_msg_set(C, NULL);
1403
1404   /* dummie test */
1405   if (ot) {
1406     wmWindow *window = CTX_wm_window(C);
1407
1408     if (event == NULL) {
1409       switch (context) {
1410         case WM_OP_INVOKE_DEFAULT:
1411         case WM_OP_INVOKE_REGION_WIN:
1412         case WM_OP_INVOKE_REGION_PREVIEW:
1413         case WM_OP_INVOKE_REGION_CHANNELS:
1414         case WM_OP_INVOKE_AREA:
1415         case WM_OP_INVOKE_SCREEN:
1416           /* window is needed for invoke, cancel operator */
1417           if (window == NULL) {
1418             if (poll_only) {
1419               CTX_wm_operator_poll_msg_set(C, "Missing 'window' in context");
1420             }
1421             return 0;
1422           }
1423           else {
1424             event = window->eventstate;
1425           }
1426           break;
1427         default:
1428           event = NULL;
1429           break;
1430       }
1431     }
1432     else {
1433       switch (context) {
1434         case WM_OP_EXEC_DEFAULT:
1435         case WM_OP_EXEC_REGION_WIN:
1436         case WM_OP_EXEC_REGION_PREVIEW:
1437         case WM_OP_EXEC_REGION_CHANNELS:
1438         case WM_OP_EXEC_AREA:
1439         case WM_OP_EXEC_SCREEN:
1440           event = NULL;
1441         default:
1442           break;
1443       }
1444     }
1445
1446     switch (context) {
1447       case WM_OP_EXEC_REGION_WIN:
1448       case WM_OP_INVOKE_REGION_WIN:
1449       case WM_OP_EXEC_REGION_CHANNELS:
1450       case WM_OP_INVOKE_REGION_CHANNELS:
1451       case WM_OP_EXEC_REGION_PREVIEW:
1452       case WM_OP_INVOKE_REGION_PREVIEW: {
1453         /* forces operator to go to the region window/channels/preview, for header menus
1454          * but we stay in the same region if we are already in one
1455          */
1456         ARegion *region = CTX_wm_region(C);
1457         ScrArea *area = CTX_wm_area(C);
1458         int type = RGN_TYPE_WINDOW;
1459
1460         switch (context) {
1461           case WM_OP_EXEC_REGION_CHANNELS:
1462           case WM_OP_INVOKE_REGION_CHANNELS:
1463             type = RGN_TYPE_CHANNELS;
1464             break;
1465
1466           case WM_OP_EXEC_REGION_PREVIEW:
1467           case WM_OP_INVOKE_REGION_PREVIEW:
1468             type = RGN_TYPE_PREVIEW;
1469             break;
1470
1471           case WM_OP_EXEC_REGION_WIN:
1472           case WM_OP_INVOKE_REGION_WIN:
1473           default:
1474             type = RGN_TYPE_WINDOW;
1475             break;
1476         }
1477
1478         if (!(region && region->regiontype == type) && area) {
1479           ARegion *ar1;
1480           if (type == RGN_TYPE_WINDOW) {
1481             ar1 = BKE_area_find_region_active_win(area);
1482           }
1483           else {
1484             ar1 = BKE_area_find_region_type(area, type);
1485           }
1486
1487           if (ar1) {
1488             CTX_wm_region_set(C, ar1);
1489           }
1490         }
1491
1492         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1493
1494         /* set region back */
1495         CTX_wm_region_set(C, region);
1496
1497         return retval;
1498       }
1499       case WM_OP_EXEC_AREA:
1500       case WM_OP_INVOKE_AREA: {
1501         /* remove region from context */
1502         ARegion *region = CTX_wm_region(C);
1503
1504         CTX_wm_region_set(C, NULL);
1505         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1506         CTX_wm_region_set(C, region);
1507
1508         return retval;
1509       }
1510       case WM_OP_EXEC_SCREEN:
1511       case WM_OP_INVOKE_SCREEN: {
1512         /* remove region + area from context */
1513         ARegion *region = CTX_wm_region(C);
1514         ScrArea *area = CTX_wm_area(C);
1515
1516         CTX_wm_region_set(C, NULL);
1517         CTX_wm_area_set(C, NULL);
1518         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1519         CTX_wm_area_set(C, area);
1520         CTX_wm_region_set(C, region);
1521
1522         return retval;
1523       }
1524       case WM_OP_EXEC_DEFAULT:
1525       case WM_OP_INVOKE_DEFAULT:
1526         return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1527     }
1528   }
1529
1530   return 0;
1531 }
1532
1533 /* invokes operator in context */
1534 int WM_operator_name_call_ptr(bContext *C,
1535                               wmOperatorType *ot,
1536                               short context,
1537                               PointerRNA *properties)
1538 {
1539   BLI_assert(ot == WM_operatortype_find(ot->idname, true));
1540   return wm_operator_call_internal(C, ot, properties, NULL, context, false, NULL);
1541 }
1542 int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
1543 {
1544   wmOperatorType *ot = WM_operatortype_find(opstring, 0);
1545   if (ot) {
1546     return WM_operator_name_call_ptr(C, ot, context, properties);
1547   }
1548
1549   return 0;
1550 }
1551
1552 int WM_operator_name_call_with_properties(struct bContext *C,
1553                                           const char *opstring,
1554                                           short context,
1555                                           struct IDProperty *properties)
1556 {
1557   PointerRNA props_ptr;
1558   wmOperatorType *ot = WM_operatortype_find(opstring, false);
1559   RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
1560   return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
1561 }
1562
1563 /**
1564  * Call an existent menu. The menu can be created in C or Python.
1565  */
1566 void WM_menu_name_call(bContext *C, const char *menu_name, short context)
1567 {
1568   wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
1569   PointerRNA ptr;
1570   WM_operator_properties_create_ptr(&ptr, ot);
1571   RNA_string_set(&ptr, "name", menu_name);
1572   WM_operator_name_call_ptr(C, ot, context, &ptr);
1573   WM_operator_properties_free(&ptr);
1574 }
1575
1576 /**
1577  * Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context.
1578  *
1579  * - #wmOperatorType is used instead of operator name since python already has the operator type.
1580  * - `poll()` must be called by python before this runs.
1581  * - reports can be passed to this function (so python can report them as exceptions).
1582  */
1583 int WM_operator_call_py(bContext *C,
1584                         wmOperatorType *ot,
1585                         short context,
1586                         PointerRNA *properties,
1587                         ReportList *reports,
1588                         const bool is_undo)
1589 {
1590   int retval = OPERATOR_CANCELLED;
1591
1592 #if 0
1593   wmOperator *op;
1594   op = wm_operator_create(wm, ot, properties, reports);
1595
1596   if (op->type->exec) {
1597     if (is_undo && op->type->flag & OPTYPE_UNDO) {
1598       wm->op_undo_depth++;
1599     }
1600
1601     retval = op->type->exec(C, op);
1602     OPERATOR_RETVAL_CHECK(retval);
1603
1604     if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1605       wm->op_undo_depth--;
1606     }
1607   }
1608   else {
1609     CLOG_WARN(WM_LOG_OPERATORS,
1610               "\"%s\" operator has no exec function, Python cannot call it",
1611               op->type->name);
1612   }
1613
1614 #endif
1615
1616   /* not especially nice using undo depth here, its used so py never
1617    * triggers undo or stores operators last used state.
1618    *
1619    * we could have some more obvious way of doing this like passing a flag.
1620    */
1621   wmWindowManager *wm = CTX_wm_manager(C);
1622   if (!is_undo && wm) {
1623     wm->op_undo_depth++;
1624   }
1625
1626   retval = wm_operator_call_internal(C, ot, properties, reports, context, false, NULL);
1627
1628   if (!is_undo && wm && (wm == CTX_wm_manager(C))) {
1629     wm->op_undo_depth--;
1630   }
1631
1632   return retval;
1633 }
1634
1635 /** \} */
1636
1637 /* -------------------------------------------------------------------- */
1638 /** \name Handler Types
1639  *
1640  * General API for different handler types.
1641  * \{ */
1642
1643 /* future extra customadata free? */
1644 void wm_event_free_handler(wmEventHandler *handler)
1645 {
1646   MEM_freeN(handler);
1647 }
1648
1649 /* only set context when area/region is part of screen */
1650 static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
1651 {
1652   wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C);
1653   /* It's probably fine to always use WM_window_get_active_screen() to get the screen. But this
1654    * code has been getting it through context since forever, so play safe and stick to that when
1655    * possible. */
1656   bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C);
1657
1658   if (screen && handler->op) {
1659     if (handler->context.area == NULL) {
1660       CTX_wm_area_set(C, NULL);
1661     }
1662     else {
1663       ScrArea *area = NULL;
1664
1665       ED_screen_areas_iter (win, screen, area_iter) {
1666         if (area_iter == handler->context.area) {
1667           area = area_iter;
1668           break;
1669         }
1670       }
1671
1672       if (area == NULL) {
1673         /* when changing screen layouts with running modal handlers (like render display), this
1674          * is not an error to print */
1675         if (handler->op == NULL) {
1676           CLOG_ERROR(WM_LOG_HANDLERS,
1677                      "internal error: handler (%s) has invalid area",
1678                      handler->op->type->idname);
1679         }
1680       }
1681       else {
1682         ARegion *region;
1683         wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL;
1684         CTX_wm_area_set(C, area);
1685
1686         if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
1687           region = BKE_area_find_region_xy(area, handler->context.region_type, event->x, event->y);
1688           if (region) {
1689             handler->context.region = region;
1690           }
1691         }
1692         else {
1693           region = NULL;
1694         }
1695
1696         if (region == NULL) {
1697           for (region = area->regionbase.first; region; region = region->next) {
1698             if (region == handler->context.region) {
1699               break;
1700             }
1701           }
1702         }
1703
1704         /* XXX no warning print here, after full-area and back regions are remade */
1705         if (region) {
1706           CTX_wm_region_set(C, region);
1707         }
1708       }
1709     }
1710   }
1711 }
1712
1713 /* called on exit or remove area, only here call cancel callback */
1714 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
1715 {
1716   wmEventHandler *handler_base;
1717   wmWindowManager *wm = CTX_wm_manager(C);
1718
1719   /* C is zero on freeing database, modal handlers then already were freed */
1720   while ((handler_base = BLI_pophead(handlers))) {
1721     BLI_assert(handler_base->type != 0);
1722     if (handler_base->type == WM_HANDLER_TYPE_OP) {
1723       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1724       if (handler->op) {
1725         wmWindow *win = CTX_wm_window(C);
1726         if (handler->op->type->cancel) {
1727           ScrArea *area = CTX_wm_area(C);
1728           ARegion *region = CTX_wm_region(C);
1729
1730           wm_handler_op_context(C, handler, win->eventstate);
1731
1732           if (handler->op->type->flag & OPTYPE_UNDO) {
1733             wm->op_undo_depth++;
1734           }
1735
1736           handler->op->type->cancel(C, handler->op);
1737
1738           if (handler->op->type->flag & OPTYPE_UNDO) {
1739             wm->op_undo_depth--;
1740           }
1741
1742           CTX_wm_area_set(C, area);
1743           CTX_wm_region_set(C, region);
1744         }
1745
1746         WM_cursor_grab_disable(win, NULL);
1747         WM_operator_free(handler->op);
1748       }
1749     }
1750     else if (handler_base->type == WM_HANDLER_TYPE_UI) {
1751       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
1752
1753       if (handler->remove_fn) {
1754         ScrArea *area = CTX_wm_area(C);
1755         ARegion *region = CTX_wm_region(C);
1756         ARegion *menu = CTX_wm_menu(C);
1757
1758         if (handler->context.area) {
1759           CTX_wm_area_set(C, handler->context.area);
1760         }
1761         if (handler->context.region) {
1762           CTX_wm_region_set(C, handler->context.region);
1763         }
1764         if (handler->context.menu) {
1765           CTX_wm_menu_set(C, handler->context.menu);
1766         }
1767
1768         handler->remove_fn(C, handler->user_data);
1769
1770         CTX_wm_area_set(C, area);
1771         CTX_wm_region_set(C, region);
1772         CTX_wm_menu_set(C, menu);
1773       }
1774     }
1775
1776     wm_event_free_handler(handler_base);
1777   }
1778 }
1779
1780 static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
1781 {
1782   if (kmi->flag & KMI_INACTIVE) {
1783     return false;
1784   }
1785
1786   if (winevent->is_repeat) {
1787     if (kmi->flag & KMI_REPEAT_IGNORE) {
1788       return false;
1789     }
1790   }
1791
1792   const int kmitype = WM_userdef_event_map(kmi->type);
1793
1794   /* the matching rules */
1795   if (kmitype == KM_TEXTINPUT) {
1796     if (winevent->val == KM_PRESS) { /* prevent double clicks */
1797       /* NOT using ISTEXTINPUT anymore because (at least on Windows) some key codes above 255
1798        * could have printable ascii keys - BUG [#30479] */
1799       if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) {
1800         return true;
1801       }
1802     }
1803   }
1804
1805   if (kmitype != KM_ANY) {
1806     if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
1807       const wmTabletData *wmtab = &winevent->tablet;
1808
1809       if (winevent->type != LEFTMOUSE) {
1810         /* tablet events can occur on hover + keypress */
1811         return false;
1812       }
1813       else if ((kmitype == TABLET_STYLUS) && (wmtab->active != EVT_TABLET_STYLUS)) {
1814         return false;
1815       }
1816       else if ((kmitype == TABLET_ERASER) && (wmtab->active != EVT_TABLET_ERASER)) {
1817         return false;
1818       }
1819     }
1820     else {
1821       if (winevent->type != kmitype) {
1822         return false;
1823       }
1824     }
1825   }
1826
1827   if (kmi->val != KM_ANY) {
1828     if (winevent->val != kmi->val) {
1829       return false;
1830     }
1831   }
1832
1833   /* Modifiers also check bits, so it allows modifier order.
1834    * Account for rare case of when these keys are used as the 'type' not as modifiers. */
1835   if (kmi->shift != KM_ANY) {
1836     if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) &&
1837         !ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
1838       return false;
1839     }
1840   }
1841   if (kmi->ctrl != KM_ANY) {
1842     if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) &&
1843         !ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
1844       return false;
1845     }
1846   }
1847   if (kmi->alt != KM_ANY) {
1848     if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) &&
1849         !ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) {
1850       return false;
1851     }
1852   }
1853   if (kmi->oskey != KM_ANY) {
1854     if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) &&
1855         (winevent->type != EVT_OSKEY)) {
1856       return false;
1857     }
1858   }
1859
1860   /* Only keymap entry with keymodifier is checked,
1861    * means all keys without modifier get handled too. */
1862   /* That is currently needed to make overlapping events work (when you press A - G fast or so). */
1863   if (kmi->keymodifier) {
1864     if (winevent->keymodifier != kmi->keymodifier) {
1865       return false;
1866     }
1867   }
1868
1869   return true;
1870 }
1871
1872 static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap,
1873                                                       wmOperator *op,
1874                                                       const wmEvent *event)
1875 {
1876   LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
1877     /* Should already be handled by #wm_user_modal_keymap_set_items. */
1878     BLI_assert(kmi->propvalue_str[0] == '\0');
1879     if (wm_eventmatch(event, kmi)) {
1880       if ((keymap->poll_modal_item == NULL) || (keymap->poll_modal_item(op, kmi->propvalue))) {
1881         return kmi;
1882       }
1883     }
1884   }
1885   return NULL;
1886 }
1887
1888 /**
1889  * This function prepares events for use with #wmOperatorType.modal by:
1890  *
1891  * - Matching keymap items with the operators modal keymap.
1892  * - Converting double click events into press events,
1893  *   allowing them to be restored when the events aren't handled.
1894  *
1895  *   This is done since we only want to use double click events to match key-map items,
1896  *   allowing modal functions to check for press/release events without having to interpret them.
1897  */
1898 static void wm_event_modalkeymap_begin(const bContext *C,
1899                                        wmOperator *op,
1900                                        wmEvent *event,
1901                                        bool *dbl_click_disabled)
1902 {
1903   BLI_assert(event->type != EVT_MODAL_MAP);
1904
1905   /* support for modal keymap in macros */
1906   if (op->opm) {
1907     op = op->opm;
1908   }
1909
1910   if (op->type->modalkeymap) {
1911     wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1912     wmKeyMapItem *kmi = NULL;
1913
1914     const wmEvent *event_match = NULL;
1915     wmEvent event_no_dbl_click;
1916
1917     if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, event))) {
1918       event_match = event;
1919     }
1920     else if (event->val == KM_DBL_CLICK) {
1921       event_no_dbl_click = *event;
1922       event_no_dbl_click.val = KM_PRESS;
1923       if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, &event_no_dbl_click))) {
1924         event_match = &event_no_dbl_click;
1925       }
1926     }
1927
1928     if (event_match != NULL) {
1929       event->prevtype = event_match->type;
1930       event->prevval = event_match->val;
1931       event->type = EVT_MODAL_MAP;
1932       event->val = kmi->propvalue;
1933
1934       /* Avoid double-click events even in the case of 'EVT_MODAL_MAP',
1935        * since it's possible users configure double-click keymap items
1936        * which would break when modal functions expect press/release. */
1937       if (event->prevtype == KM_DBL_CLICK) {
1938         event->prevtype = KM_PRESS;
1939         *dbl_click_disabled = true;
1940       }
1941     }
1942   }
1943
1944   if (event->type != EVT_MODAL_MAP) {
1945     /* This bypass just disables support for double-click in modal handlers. */
1946     if (event->val == KM_DBL_CLICK) {
1947       event->val = KM_PRESS;
1948       *dbl_click_disabled = true;
1949     }
1950   }
1951 }
1952
1953 /**
1954  * Restore changes from #wm_event_modalkeymap_begin
1955  *
1956  * \warning bad hacking event system...
1957  * better restore event type for checking of #KM_CLICK for example.
1958  * Modal maps could use different method (ton).
1959  */
1960 static void wm_event_modalkeymap_end(wmEvent *event, bool dbl_click_disabled)
1961 {
1962   if (event->type == EVT_MODAL_MAP) {
1963     event->type = event->prevtype;
1964     event->prevtype = 0;
1965     event->val = event->prevval;
1966     event->prevval = 0;
1967   }
1968
1969   if (dbl_click_disabled) {
1970     event->val = KM_DBL_CLICK;
1971   }
1972 }
1973
1974 /* Warning: this function removes a modal handler, when finished */
1975 static int wm_handler_operator_call(bContext *C,
1976                                     ListBase *handlers,
1977                                     wmEventHandler *handler_base,
1978                                     wmEvent *event,
1979                                     PointerRNA *properties,
1980                                     const char *kmi_idname)
1981 {
1982   int retval = OPERATOR_PASS_THROUGH;
1983
1984   /* derived, modal or blocking operator */
1985   if ((handler_base->type == WM_HANDLER_TYPE_OP) &&
1986       (((wmEventHandler_Op *)handler_base)->op != NULL)) {
1987     wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1988     wmOperator *op = handler->op;
1989     wmOperatorType *ot = op->type;
1990
1991     if (!wm_operator_check_locked_interface(C, ot)) {
1992       /* Interface is locked and operator is not allowed to run,
1993        * nothing to do in this case.
1994        */
1995     }
1996     else if (ot->modal) {
1997       /* we set context to where modal handler came from */
1998       wmWindowManager *wm = CTX_wm_manager(C);
1999       ScrArea *area = CTX_wm_area(C);
2000       ARegion *region = CTX_wm_region(C);
2001       bool dbl_click_disabled = false;
2002
2003       wm_handler_op_context(C, handler, event);
2004       wm_region_mouse_co(C, event);
2005       wm_event_modalkeymap_begin(C, op, event, &dbl_click_disabled);
2006
2007       if (ot->flag & OPTYPE_UNDO) {
2008         wm->op_undo_depth++;
2009       }
2010
2011       /* warning, after this call all context data and 'event' may be freed. see check below */
2012       retval = ot->modal(C, op, event);
2013       OPERATOR_RETVAL_CHECK(retval);
2014
2015       /* when this is _not_ the case the modal modifier may have loaded
2016        * a new blend file (demo mode does this), so we have to assume
2017        * the event, operator etc have all been freed. - campbell */
2018       if (CTX_wm_manager(C) == wm) {
2019
2020         wm_event_modalkeymap_end(event, dbl_click_disabled);
2021
2022         if (ot->flag & OPTYPE_UNDO) {
2023           wm->op_undo_depth--;
2024         }
2025
2026         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2027           wm_operator_reports(C, op, retval, false);
2028
2029           if (op->type->modalkeymap) {
2030             wmWindow *win = CTX_wm_window(C);
2031             WM_window_status_area_tag_redraw(win);
2032           }
2033         }
2034         else {
2035           /* not very common, but modal operators may report before finishing */
2036           if (!BLI_listbase_is_empty(&op->reports->list)) {
2037             wm_add_reports(op->reports);
2038           }
2039         }
2040
2041         /* important to run 'wm_operator_finished' before NULLing the context members */
2042         if (retval & OPERATOR_FINISHED) {
2043           wm_operator_finished(C, op, false, true);
2044           handler->op = NULL;
2045         }
2046         else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2047           WM_operator_free(op);
2048           handler->op = NULL;
2049         }
2050
2051         /* putting back screen context, reval can pass trough after modal failures! */
2052         if ((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
2053           CTX_wm_area_set(C, area);
2054           CTX_wm_region_set(C, region);
2055         }
2056         else {
2057           /* this special cases is for areas and regions that get removed */
2058           CTX_wm_area_set(C, NULL);
2059           CTX_wm_region_set(C, NULL);
2060         }
2061
2062         /* update gizmos during modal handlers */
2063         wm_gizmomaps_handled_modal_update(C, event, handler);
2064
2065         /* remove modal handler, operator itself should have been canceled and freed */
2066         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2067           WM_cursor_grab_disable(CTX_wm_window(C), NULL);
2068
2069           BLI_remlink(handlers, handler);
2070           wm_event_free_handler(&handler->head);
2071
2072           /* prevent silly errors from operator users */
2073           // retval &= ~OPERATOR_PASS_THROUGH;
2074         }
2075       }
2076     }
2077     else {
2078       CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
2079     }
2080   }
2081   else {
2082     wmOperatorType *ot = WM_operatortype_find(kmi_idname, 0);
2083
2084     if (ot && wm_operator_check_locked_interface(C, ot)) {
2085       bool use_last_properties = true;
2086       PointerRNA tool_properties = {0};
2087
2088       bToolRef *keymap_tool = NULL;
2089       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2090         keymap_tool = ((wmEventHandler_Keymap *)handler_base)->keymap_tool;
2091       }
2092       else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2093         wmGizmoMap *gizmo_map = ((wmEventHandler_Gizmo *)handler_base)->gizmo_map;
2094         wmGizmo *gz = wm_gizmomap_highlight_get(gizmo_map);
2095         if (gz && (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT)) {
2096           keymap_tool = WM_toolsystem_ref_from_context(C);
2097         }
2098       }
2099
2100       const bool is_tool = (keymap_tool != NULL);
2101       const bool use_tool_properties = is_tool;
2102
2103       if (use_tool_properties) {
2104         WM_toolsystem_ref_properties_init_for_keymap(
2105             keymap_tool, &tool_properties, properties, ot);
2106         properties = &tool_properties;
2107         use_last_properties = false;
2108       }
2109
2110       retval = wm_operator_invoke(C, ot, event, properties, NULL, false, use_last_properties);
2111
2112       if (use_tool_properties) {
2113         WM_operator_properties_free(&tool_properties);
2114       }
2115
2116       /* Link gizmo if 'WM_GIZMOGROUPTYPE_TOOL_INIT' is set. */
2117       if (retval & OPERATOR_FINISHED) {
2118         if (is_tool) {
2119           bToolRef_Runtime *tref_rt = keymap_tool->runtime;
2120           if (tref_rt->gizmo_group[0]) {
2121             const char *idname = tref_rt->gizmo_group;
2122             wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
2123             if (gzgt != NULL) {
2124               if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) != 0) {
2125                 ARegion *region = CTX_wm_region(C);
2126                 if (region != NULL) {
2127                   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
2128                   WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
2129                   wmGizmoGroup *gzgroup = WM_gizmomaptype_group_init_runtime_with_region(
2130                       gzmap_type, gzgt, region);
2131                   /* We can't rely on drawing to initialize gizmo's since disabling
2132                    * overlays/gizmos will prevent pre-drawing setup calls. (see T60905) */
2133                   WM_gizmogroup_ensure_init(C, gzgroup);
2134                 }
2135               }
2136             }
2137           }
2138         }
2139       }
2140       /* Done linking gizmo. */
2141     }
2142   }
2143   /* Finished and pass through flag as handled */
2144
2145   /* Finished and pass through flag as handled */
2146   if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH)) {
2147     return WM_HANDLER_HANDLED;
2148   }
2149
2150   /* Modal unhandled, break */
2151   if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL)) {
2152     return (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2153   }
2154
2155   if (retval & OPERATOR_PASS_THROUGH) {
2156     return WM_HANDLER_CONTINUE;
2157   }
2158
2159   return WM_HANDLER_BREAK;
2160 }
2161
2162 /* Fileselect handlers are only in the window queue,
2163  * so it's safe to switch screens or area types. */
2164 static int wm_handler_fileselect_do(bContext *C,
2165                                     ListBase *handlers,
2166                                     wmEventHandler_Op *handler,
2167                                     int val)
2168 {
2169   wmWindowManager *wm = CTX_wm_manager(C);
2170   int action = WM_HANDLER_CONTINUE;
2171
2172   switch (val) {
2173     case EVT_FILESELECT_FULL_OPEN: {
2174       wmWindow *win = CTX_wm_window(C);
2175       ScrArea *area;
2176
2177       if ((area = ED_screen_temp_space_open(C,
2178                                             IFACE_("Blender File View"),
2179                                             WM_window_pixels_x(win) / 2,
2180                                             WM_window_pixels_y(win) / 2,
2181                                             U.file_space_data.temp_win_sizex * UI_DPI_FAC,
2182                                             U.file_space_data.temp_win_sizey * UI_DPI_FAC,
2183                                             SPACE_FILE,
2184                                             U.filebrowser_display_type,
2185                                             true))) {
2186         ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
2187
2188         BLI_assert(area->spacetype == SPACE_FILE);
2189
2190         region_header->flag |= RGN_FLAG_HIDDEN;
2191         /* Header on bottom, AZone triangle to toggle header looks misplaced at the top */
2192         region_header->alignment = RGN_ALIGN_BOTTOM;
2193
2194         /* settings for filebrowser, sfile is not operator owner but sends events */
2195         SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
2196         sfile->op = handler->op;
2197
2198         ED_fileselect_set_params_from_userdef(sfile);
2199       }
2200       else {
2201         BKE_report(&wm->reports, RPT_ERROR, "Failed to open window!");
2202         return OPERATOR_CANCELLED;
2203       }
2204
2205       action = WM_HANDLER_BREAK;
2206       break;
2207     }
2208
2209     case EVT_FILESELECT_EXEC:
2210     case EVT_FILESELECT_CANCEL:
2211     case EVT_FILESELECT_EXTERNAL_CANCEL: {
2212       wmWindow *ctx_win = CTX_wm_window(C);
2213
2214       /* remlink now, for load file case before removing*/
2215       BLI_remlink(handlers, handler);
2216
2217       if (val == EVT_FILESELECT_EXTERNAL_CANCEL) {
2218         /* The window might have been freed already. */
2219         if (BLI_findindex(&wm->windows, handler->context.win) == -1) {
2220           handler->context.win = NULL;
2221         }
2222       }
2223       else {
2224         wmWindow *temp_win;
2225         ScrArea *ctx_area = CTX_wm_area(C);
2226
2227         for (temp_win = wm->windows.first; temp_win; temp_win = temp_win->next) {
2228           bScreen *screen = WM_window_get_active_screen(temp_win);
2229           ScrArea *file_area = screen->areabase.first;
2230
2231           if (screen->temp && (file_area->spacetype == SPACE_FILE)) {
2232             int win_size[2];
2233             bool is_maximized;
2234             ED_fileselect_window_params_get(temp_win, win_size, &is_maximized);
2235             ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized);
2236
2237             if (BLI_listbase_is_single(&file_area->spacedata)) {
2238               BLI_assert(ctx_win != temp_win);
2239
2240               wm_window_close(C, wm, temp_win);
2241
2242               CTX_wm_window_set(C, ctx_win);  // wm_window_close() NULLs.
2243               /* Some operators expect a drawable context (for EVT_FILESELECT_EXEC) */
2244               wm_window_make_drawable(wm, ctx_win);
2245               /* Ensure correct cursor position, otherwise, popups may close immediately after
2246                * opening (UI_BLOCK_MOVEMOUSE_QUIT) */
2247               wm_get_cursor_position(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y);
2248               wm->winactive = ctx_win; /* Reports use this... */
2249               if (handler->context.win == temp_win) {
2250                 handler->context.win = NULL;
2251               }
2252             }
2253             else if (file_area->full) {
2254               ED_screen_full_prevspace(C, file_area);
2255             }
2256             else {
2257               ED_area_prevspace(C, file_area);
2258             }
2259
2260             break;
2261           }
2262         }
2263
2264         if (!temp_win && ctx_area->full) {
2265           ED_fileselect_params_to_userdef(ctx_area->spacedata.first, NULL, false);
2266           ED_screen_full_prevspace(C, ctx_area);
2267         }
2268       }
2269
2270       wm_handler_op_context(C, handler, ctx_win->eventstate);
2271
2272       /* needed for UI_popup_menu_reports */
2273
2274       if (val == EVT_FILESELECT_EXEC) {
2275         int retval;
2276
2277         if (handler->op->type->flag & OPTYPE_UNDO) {
2278           wm->op_undo_depth++;
2279         }
2280
2281         retval = handler->op->type->exec(C, handler->op);
2282
2283         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2284         if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
2285           wm->op_undo_depth--;
2286         }
2287
2288         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2289         if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
2290           if (handler->op->type->flag & OPTYPE_UNDO) {
2291             ED_undo_push_op(C, handler->op);
2292           }
2293           else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED) {
2294             ED_undo_grouped_push_op(C, handler->op);
2295           }
2296         }
2297
2298         if (handler->op->reports->list.first) {
2299
2300           /* FIXME, temp setting window, this is really bad!
2301            * only have because lib linking errors need to be seen by users :(
2302            * it can be removed without breaking anything but then no linking errors - campbell */
2303           wmWindow *win_prev = CTX_wm_window(C);
2304           ScrArea *area_prev = CTX_wm_area(C);
2305           ARegion *region_prev = CTX_wm_region(C);
2306
2307           if (win_prev == NULL) {
2308             CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
2309           }
2310
2311           BKE_report_print_level_set(handler->op->reports, RPT_WARNING);
2312           UI_popup_menu_reports(C, handler->op->reports);
2313
2314           /* XXX - copied from 'wm_operator_finished()' */
2315           /* add reports to the global list, otherwise they are not seen */
2316           BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
2317
2318           /* more hacks, since we meddle with reports, banner display doesn't happen automatic */
2319           WM_report_banner_show();
2320
2321           CTX_wm_window_set(C, win_prev);
2322           CTX_wm_area_set(C, area_prev);
2323           CTX_wm_region_set(C, region_prev);
2324         }
2325
2326         /* for WM_operator_pystring only, custom report handling is done above */
2327         wm_operator_reports(C, handler->op, retval, true);
2328
2329         if (retval & OPERATOR_FINISHED) {
2330           WM_operator_last_properties_store(handler->op);
2331         }
2332
2333         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2334           WM_operator_free(handler->op);
2335         }
2336       }
2337       else {
2338         if (handler->op->type->cancel) {
2339           if (handler->op->type->flag & OPTYPE_UNDO) {
2340             wm->op_undo_depth++;
2341           }
2342
2343           handler->op->type->cancel(C, handler->op);
2344
2345           if (handler->op->type->flag & OPTYPE_UNDO) {
2346             wm->op_undo_depth--;
2347           }
2348         }
2349
2350         WM_operator_free(handler->op);
2351       }
2352
2353       CTX_wm_area_set(C, NULL);
2354
2355       wm_event_free_handler(&handler->head);
2356
2357       action = WM_HANDLER_BREAK;
2358       break;
2359     }
2360   }
2361
2362   return action;
2363 }
2364
2365 static int wm_handler_fileselect_call(bContext *C,
2366                                       ListBase *handlers,
2367                                       wmEventHandler_Op *handler,
2368                                       const wmEvent *event)
2369 {
2370   int action = WM_HANDLER_CONTINUE;
2371
2372   if (event->type != EVT_FILESELECT) {
2373     return action;
2374   }
2375   if (handler->op != (wmOperator *)event->customdata) {
2376     return action;
2377   }
2378
2379   return wm_handler_fileselect_do(C, handlers, handler, event->val);
2380 }
2381
2382 static int wm_action_not_handled(int action)
2383 {
2384   return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2385 }
2386
2387 #define PRINT \
2388   if (do_debug_handler) \
2389   printf
2390
2391 static int wm_handlers_do_keymap_with_keymap_handler(
2392     /* From 'wm_handlers_do_intern' */
2393     bContext *C,
2394     wmEvent *event,
2395     ListBase *handlers,
2396     wmEventHandler_Keymap *handler,
2397     /* Additional. */
2398     wmKeyMap *keymap,
2399     const bool do_debug_handler)
2400 {
2401   int action = WM_HANDLER_CONTINUE;
2402
2403   if (keymap == NULL) {
2404     /* Only callback is allowed to have NULL keymaps. */
2405     BLI_assert(handler->dynamic.keymap_fn);
2406   }
2407   else {
2408     PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2409
2410     if (WM_keymap_poll(C, keymap)) {
2411
2412       PRINT("pass\n");
2413
2414       LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
2415         if (wm_eventmatch(event, kmi)) {
2416           struct wmEventHandler_KeymapPost keymap_post = handler->post;
2417
2418           PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2419
2420           action |= wm_handler_operator_call(
2421               C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
2422
2423           if (action & WM_HANDLER_BREAK) {
2424             /* not always_pass here, it denotes removed handler_base */
2425             CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
2426             if (keymap_post.post_fn != NULL) {
2427               keymap_post.post_fn(keymap, kmi, keymap_post.user_data);
2428             }
2429             break;
2430           }
2431           else {
2432             if (action & WM_HANDLER_HANDLED) {
2433               CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
2434             }
2435             else {
2436               CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
2437             }
2438           }
2439         }
2440       }
2441     }
2442     else {
2443       PRINT("fail\n");
2444     }
2445   }
2446
2447   return action;
2448 }
2449
2450 static int wm_handlers_do_keymap_with_gizmo_handler(
2451     /* From 'wm_handlers_do_intern' */
2452     bContext *C,
2453     wmEvent *event,
2454     ListBase *handlers,
2455     wmEventHandler_Gizmo *handler,
2456     /* Additional. */
2457     wmGizmoGroup *gzgroup,
2458     wmKeyMap *keymap,
2459     const bool do_debug_handler,
2460     bool *r_keymap_poll)
2461 {
2462   int action = WM_HANDLER_CONTINUE;
2463   bool keymap_poll = false;
2464   wmKeyMapItem *kmi;
2465
2466   PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2467
2468   if (WM_keymap_poll(C, keymap)) {
2469     keymap_poll = true;
2470     PRINT("pass\n");
2471     for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2472       if (wm_eventmatch(event, kmi)) {
2473         PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2474
2475         CTX_wm_gizmo_group_set(C, gzgroup);
2476
2477         /* handler->op is called later, we want keymap op to be triggered here */
2478         action |= wm_handler_operator_call(
2479             C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
2480
2481         CTX_wm_gizmo_group_set(C, NULL);
2482
2483         if (action & WM_HANDLER_BREAK) {
2484           if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2485             printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2486           }
2487           break;
2488         }
2489         else {
2490           if (action & WM_HANDLER_HANDLED) {
2491             if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2492               printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2493             }
2494           }
2495           else {
2496             PRINT("%s:       un-handled '%s'\n", __func__, kmi->idname);
2497           }
2498         }
2499       }
2500     }
2501   }
2502   else {
2503     PRINT("fail\n");
2504   }
2505
2506   if (r_keymap_poll) {
2507     *r_keymap_poll = keymap_poll;
2508   }
2509
2510   return action;
2511 }
2512
2513 static int wm_handlers_do_gizmo_handler(bContext *C,
2514                                         wmWindowManager *wm,
2515                                         wmEventHandler_Gizmo *handler,
2516                                         wmEvent *event,
2517                                         ListBase *handlers,
2518                                         const bool do_debug_handler)
2519 {
2520   int action = WM_HANDLER_CONTINUE;
2521   ScrArea *area = CTX_wm_area(C);
2522   ARegion *region = CTX_wm_region(C);
2523   wmGizmoMap *gzmap = handler->gizmo_map;
2524   BLI_assert(gzmap != NULL);
2525   wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
2526
2527   /* Needed so UI blocks over gizmos don't let events fall through to the gizmos,
2528    * noticeable for the node editor - where dragging on a node should move it, see: T73212.
2529    * note we still allow for starting the gizmo drag outside, then travel 'inside' the node */
2530   if (region->type->clip_gizmo_events_by_ui) {
2531     if (UI_region_block_find_mouse_over(region, &event->x, true)) {
2532       if (gz != NULL && event->type != EVT_GIZMO_UPDATE) {
2533         WM_tooltip_clear(C, CTX_wm_window(C));
2534         wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
2535       }
2536       return action;
2537     }
2538   }
2539
2540   if (region->gizmo_map != handler->gizmo_map) {
2541     WM_gizmomap_tag_refresh(handler->gizmo_map);
2542   }
2543
2544   wm_gizmomap_handler_context_gizmo(C, handler);
2545   wm_region_mouse_co(C, event);
2546
2547   /* Drag events use the previous click location to highlight the gizmos,
2548    * Get the highlight again in case the user dragged off the gizmo. */
2549   const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
2550   const bool is_event_modifier = ISKEYMODIFIER(event->type);
2551
2552   bool handle_highlight = false;
2553   bool handle_keymap = false;
2554
2555   /* handle gizmo highlighting */
2556   if (!wm_gizmomap_modal_get(gzmap) &&
2557       ((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag)) {
2558     handle_highlight = true;
2559     if (is_event_modifier || is_event_drag) {
2560       handle_keymap = true;
2561     }
2562   }
2563   else {
2564     handle_keymap = true;
2565   }
2566
2567   if (handle_highlight) {
2568     struct {
2569       wmGizmo *gz;
2570       int part;
2571     } prev = {
2572         .gz = gz,
2573         .part = gz ? gz->highlight_part : 0,
2574     };
2575     int part = -1;
2576     gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
2577
2578     /* If no gizmos are/were active, don't clear tool-tips. */
2579     if (gz || prev.gz) {
2580       if ((prev.gz != gz) || (prev.part != part)) {
2581         WM_tooltip_clear(C, CTX_wm_window(C));
2582       }
2583     }
2584
2585     if (wm_gizmomap_highlight_set(gzmap, C, gz, part)) {
2586       if (gz != NULL) {
2587         if (U.flag & USER_TOOLTIPS) {
2588           WM_tooltip_timer_init(C, CTX_wm_window(C), area, region, WM_gizmomap_tooltip_init);
2589         }
2590       }
2591     }
2592   }
2593
2594   /* Don't use from now on. */
2595   bool is_event_handle_all = gz && (gz->flag & WM_GIZMO_EVENT_HANDLE_ALL);
2596
2597   if (handle_keymap) {
2598     /* Handle highlight gizmo. */
2599     if (gz != NULL) {
2600       bool keymap_poll = false;
2601       wmGizmoGroup *gzgroup = gz->parent_gzgroup;
2602       wmKeyMap *keymap = WM_keymap_active(wm, gz->keymap ? gz->keymap : gzgroup->type->keymap);
2603       action |= wm_handlers_do_keymap_with_gizmo_handler(
2604           C, event, handlers, handler, gzgroup, keymap, do_debug_handler, &keymap_poll);
2605
2606 #ifdef USE_GIZMO_MOUSE_PRIORITY_HACK
2607       if (((action & WM_HANDLER_BREAK) == 0) && !is_event_handle_all && keymap_poll) {
2608         if ((event->val == KM_PRESS) && ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
2609
2610           wmEvent event_test_click = *event;
2611           event_test_click.val = KM_CLICK;
2612
2613           wmEvent event_test_click_drag = *event;
2614           event_test_click_drag.val = KM_CLICK_DRAG;
2615
2616           wmEvent event_test_tweak = *event;
2617           event_test_tweak.type = EVT_TWEAK_L + (event->type - LEFTMOUSE);
2618           event_test_tweak.val = KM_ANY;
2619
2620           LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
2621             if ((kmi->flag & KMI_INACTIVE) == 0) {
2622               if (wm_eventmatch(&event_test_click, kmi) ||
2623                   wm_eventmatch(&event_test_click_drag, kmi) ||
2624                   wm_eventmatch(&event_test_tweak, kmi)) {
2625                 wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
2626                 if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) {
2627                   is_event_handle_all = true;
2628                   break;
2629                 }
2630               }
2631             }
2632           }
2633         }
2634       }
2635 #endif /* USE_GIZMO_MOUSE_PRIORITY_HACK */
2636     }
2637
2638     /* Don't use from now on. */
2639     gz = NULL;
2640
2641     /* Fallback to selected gizmo (when un-handled). */
2642     if ((action & WM_HANDLER_BREAK) == 0) {
2643       if (WM_gizmomap_is_any_selected(gzmap)) {
2644         const ListBase *groups = WM_gizmomap_group_list(gzmap);
2645         LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, groups) {
2646           if (wm_gizmogroup_is_any_selected(gzgroup)) {
2647             wmKeyMap *keymap = WM_keymap_active(wm, gzgroup->type->keymap);
2648             action |= wm_handlers_do_keymap_with_gizmo_handler(
2649                 C, event, handlers, handler, gzgroup, keymap, do_debug_handler, NULL);
2650             if (action & WM_HANDLER_BREAK) {
2651               break;
2652             }
2653           }
2654         }
2655       }
2656     }
2657   }
2658
2659   if (is_event_handle_all) {
2660     if (action == WM_HANDLER_CONTINUE) {
2661       action |= WM_HANDLER_BREAK | WM_HANDLER_MODAL;
2662     }
2663   }
2664
2665   /* restore the area */
2666   CTX_wm_area_set(C, area);
2667   CTX_wm_region_set(C, region);
2668
2669   return action;
2670 }
2671
2672 /** \} */
2673
2674 /* -------------------------------------------------------------------- */
2675 /** \name Handle Single Event (All Handler Types)
2676  * \{ */
2677
2678 static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
2679 {
2680   const bool do_debug_handler =
2681       (G.debug & G_DEBUG_HANDLERS) &&
2682       /* comment this out to flood the console! (if you really want to test) */
2683       !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE);
2684
2685   wmWindowManager *wm = CTX_wm_manager(C);
2686   int action = WM_HANDLER_CONTINUE;
2687   int always_pass;
2688
2689   if (handlers == NULL) {
2690     return action;
2691   }
2692
2693   /* modal handlers can get removed in this loop, we keep the loop this way
2694    *
2695    * note: check 'handlers->first' because in rare cases the handlers can be cleared
2696    * by the event that's called, for eg:
2697    *
2698    * Calling a python script which changes the area.type, see [#32232] */
2699   for (wmEventHandler *handler_base = handlers->first, *handler_base_next;
2700        handler_base && handlers->first;
2701        handler_base = handler_base_next) {
2702     handler_base_next = handler_base->next;
2703
2704     /* during this loop, ui handlers for nested menus can tag multiple handlers free */
2705     if (handler_base->flag & WM_HANDLER_DO_FREE) {
2706       /* pass */
2707     }
2708     else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) {
2709       /* in advance to avoid access to freed event on window close */
2710       always_pass = wm_event_always_pass(event);
2711
2712       /* modal+blocking handler_base */
2713       if (handler_base->flag & WM_HANDLER_BLOCKING) {
2714         action |= WM_HANDLER_BREAK;
2715       }
2716
2717       /* Handle all types here. */
2718       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2719         wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
2720         wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
2721         action |= wm_handlers_do_keymap_with_keymap_handler(
2722             C, event, handlers, handler, keymap, do_debug_handler);
2723
2724         /* Clear the tool-tip whenever a key binding is handled, without this tool-tips
2725          * are kept when a modal operators starts (annoying but otherwise harmless). */
2726         if (action & WM_HANDLER_BREAK) {
2727           /* Window may be gone after file read. */
2728           if (CTX_wm_window(C) != NULL) {
2729             WM_tooltip_clear(C, CTX_wm_window(C));
2730           }
2731         }
2732       }
2733       else if (handler_base->type == WM_HANDLER_TYPE_UI) {
2734         wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
2735         BLI_assert(handler->handle_fn != NULL);
2736         if (!wm->is_interface_locked) {
2737           action |= wm_handler_ui_call(C, handler, event, always_pass);
2738         }
2739       }
2740       else if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
2741         wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
2742         if (!wm->is_interface_locked && event->type == EVT_DROP) {
2743           wmDropBox *drop = handler->dropboxes->first;
2744           for (; drop; drop = drop->next) {
2745             /* other drop custom types allowed */
2746             if (event->custom == EVT_DATA_DRAGDROP) {
2747               ListBase *lb = (ListBase *)event->customdata;
2748               wmDrag *drag;
2749
2750               for (drag = lb->first; drag; drag = drag->next) {
2751                 const char *tooltip = NULL;
2752                 if (drop->poll(C, drag, event, &tooltip)) {
2753                   /* Optionally copy drag information to operator properties. */
2754                   if (drop->copy) {
2755                     drop->copy(drag, drop);
2756                   }
2757
2758                   /* Pass single matched wmDrag onto the operator. */
2759                   BLI_remlink(lb, drag);
2760                   ListBase single_lb = {drag, drag};
2761                   event->customdata = &single_lb;
2762
2763                   wm_operator_call_internal(
2764                       C, drop->ot, drop->ptr, NULL, drop->opcontext, false, event);
2765                   action |= WM_HANDLER_BREAK;
2766
2767                   /* free the drags */
2768                   WM_drag_free_list(lb);
2769                   WM_drag_free_list(&single_lb);
2770
2771                   event->customdata = NULL;
2772                   event->custom = 0;
2773
2774                   /* XXX fileread case */
2775                   if (CTX_wm_window(C) == NULL) {
2776                     return action;
2777                   }
2778
2779                   /* escape from drag loop, got freed */
2780                   break;
2781                 }
2782               }
2783             }
2784           }
2785         }
2786       }
2787       else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2788         wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
2789         action |= wm_handlers_do_gizmo_handler(C, wm, handler, event, handlers, do_debug_handler);
2790       }
2791       else if (handler_base->type == WM_HANDLER_TYPE_OP) {
2792         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2793         if (handler->is_fileselect) {
2794           if (!wm->is_interface_locked) {
2795             /* screen context changes here */
2796             action |= wm_handler_fileselect_call(C, handlers, handler, event);
2797           }
2798         }
2799         else {
2800           action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL, NULL);
2801         }
2802       }
2803       else {
2804         /* Unreachable (handle all types above). */
2805         BLI_assert(0);
2806       }
2807
2808       if (action & WM_HANDLER_BREAK) {
2809         if (always_pass) {
2810           action &= ~WM_HANDLER_BREAK;
2811         }
2812         else {
2813           break;
2814         }
2815       }
2816     }
2817
2818     /* XXX fileread case, if the wm is freed then the handler's
2819      * will have been too so the code below need not run. */
2820     if (CTX_wm_window(C) == NULL) {
2821       return action;
2822     }
2823
2824     /* XXX code this for all modal ops, and ensure free only happens here */
2825
2826     /* modal ui handler can be tagged to be freed */
2827     if (BLI_findindex(handlers, handler_base) !=
2828         -1) { /* could be freed already by regular modal ops */
2829       if (handler_base->flag & WM_HANDLER_DO_FREE) {
2830         BLI_remlink(handlers, handler_base);
2831         wm_event_free_handler(handler_base);
2832       }
2833     }
2834   }
2835
2836   if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL)) {
2837     wm_cursor_arrow_move(CTX_wm_window(C), event);
2838   }
2839
2840   return action;
2841 }
2842
2843 #undef PRINT
2844
2845 /* this calls handlers twice - to solve (double-)click events */
2846 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
2847 {
2848   int action = wm_handlers_do_intern(C, event, handlers);
2849
2850   /* fileread case */
2851   if (CTX_wm_window(C) == NULL) {
2852     return action;
2853   }
2854
2855   if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2856
2857     /* Test for CLICK_DRAG events. */
2858     if (wm_action_not_handled(action)) {
2859       if (event->check_drag) {
2860         wmWindow *win = CTX_wm_window(C);
2861         if (WM_event_drag_test(event, &win->eventstate->prevclickx)) {
2862           int x = event->x;
2863           int y = event->y;
2864           short val = event->val;
2865           short type = event->type;
2866
2867           event->x = win->eventstate->prevclickx;
2868           event->y = win->eventstate->prevclicky;
2869           event->val = KM_CLICK_DRAG;
2870           event->type = win->eventstate->type;
2871
2872           CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
2873
2874           action |= wm_handlers_do_intern(C, event, handlers);
2875
2876           event->val = val;
2877           event->type = type;
2878           event->x = x;
2879           event->y = y;
2880
2881           win->eventstate->check_click = false;
2882           win->eventstate->check_drag = false;
2883         }
2884       }
2885     }
2886     else {
2887       wmWindow *win = CTX_wm_window(C);
2888       if (win) {
2889         win->eventstate->check_drag = false;
2890       }
2891     }
2892   }
2893   else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) {
2894     /* All events that don't set wmEvent.prevtype must be ignored. */
2895
2896     /* Test for CLICK events. */
2897     if (wm_action_not_handled(action)) {
2898       wmWindow *win = CTX_wm_window(C);
2899
2900       /* eventstate stores if previous event was a KM_PRESS, in case that
2901        * wasn't handled, the KM_RELEASE will become a KM_CLICK */
2902
2903       if (win != NULL) {
2904         if (event->val == KM_PRESS) {
2905           win->eventstate->check_click = true;
2906           win->eventstate->check_drag = true;
2907         }
2908         else if (event->val == KM_RELEASE) {
2909           win->eventstate->check_drag = false;
2910         }
2911       }
2912
2913       if (win && win->eventstate->prevtype == event->type) {
2914
2915         if ((event->val == KM_RELEASE) && (win->eventstate->prevval == KM_PRESS) &&
2916             (win->eventstate->check_click == true)) {
2917           if (WM_event_drag_test(event, &win->eventstate->prevclickx)) {
2918             win->eventstate->check_click = 0;
2919             win->eventstate->check_drag = 0;
2920           }
2921           else {
2922             /* Position is where the actual click happens, for more
2923              * accurate selecting in case the mouse drifts a little. */
2924             int x = event->x;
2925             int y = event->y;
2926
2927             event->x = win->eventstate->prevclickx;
2928             event->y = win->eventstate->prevclicky;
2929             event->val = KM_CLICK;
2930
2931             CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
2932
2933             action |= wm_handlers_do_intern(C, event, handlers);
2934
2935             event->val = KM_RELEASE;
2936             event->x = x;
2937             event->y = y;
2938           }
2939         }
2940         else if (event->val == KM_DBL_CLICK) {
2941           /* The underlying event is a press, so try and handle this. */
2942           event->val = KM_PRESS;
2943           action |= wm_handlers_do_intern(C, event, handlers);
2944
2945           /* revert value if not handled */
2946           if (wm_action_not_handled(action)) {
2947             event->val = KM_DBL_CLICK;
2948           }
2949         }
2950       }
2951     }
2952     else {
2953       wmWindow *win = CTX_wm_window(C);
2954       if (win) {
2955         win->eventstate->check_click = 0;
2956         win->eventstate->check_drag = 0;
2957       }
2958     }
2959   }
2960   else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) {
2961     /* Modifiers which can trigger click event's,
2962      * however we don't want this if the mouse wheel has been used, see T74607. */
2963     if (wm_action_not_handled(action)) {
2964       /* pass */
2965     }
2966     else {
2967       wmWindow *win = CTX_wm_window(C);
2968       if (win) {
2969         if (ISKEYMODIFIER(win->eventstate->type)) {
2970           win->eventstate->check_click = 0;
2971         }
2972       }
2973     }
2974   }
2975
2976   return action;
2977 }
2978
2979 /** \} */
2980
2981 /* -------------------------------------------------------------------- */
2982 /** \name Event Queue Utilities
2983  *
2984  * Utilities used by #wm_event_do_handlers.
2985  * \{ */
2986
2987 static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect)
2988 {
2989   if (wm_event_always_pass(event)) {
2990     return true;
2991   }
2992   if (BLI_rcti_isect_pt_v(rect, &event->x)) {
2993     return true;
2994   }
2995   return false;
2996 }
2997
2998 static bool wm_event_inside_region(const wmEvent *event, const ARegion *region)
2999 {
3000   if (wm_event_always_pass(event)) {
3001     return true;
3002   }
3003   return ED_region_contains_xy(region, &event->x);
3004 }
3005
3006 static ScrArea *area_event_inside(bContext *C, const int xy[2])
3007 {
3008   wmWindow *win = CTX_wm_window(C);
3009   bScreen *screen = CTX_wm_screen(C);
3010
3011   if (screen) {
3012     ED_screen_areas_iter (win, screen, area) {
3013       if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
3014         return area;
3015       }
3016     }
3017   }
3018   return NULL;
3019 }
3020
3021 static ARegion *region_event_inside(bContext *C, const int xy[2])
3022 {
3023   bScreen *screen = CTX_wm_screen(C);
3024   ScrArea *area = CTX_wm_area(C);
3025   ARegion *region;
3026
3027   if (screen && area) {
3028     for (region = area->regionbase.first; region; region = region->next) {
3029       if (BLI_rcti_isect_pt_v(&region->winrct, xy)) {
3030         return region;
3031       }
3032     }
3033   }
3034   return NULL;
3035 }
3036
3037 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *region)
3038 {
3039   if (region) {
3040     for (; pc; pc = pc->next) {
3041       if (pc->poll == NULL || pc->poll(C)) {
3042         wmWindow *win = CTX_wm_window(C);
3043         WM_paint_cursor_tag_redraw(win, region);
3044       }
3045     }
3046   }
3047 }
3048
3049 /* called on mousemove, check updates for paintcursors */
3050 /* context was set on active area and region */
3051 static void wm_paintcursor_test(bContext *C, const wmEvent *event)
3052 {
3053   wmWindowManager *wm = CTX_wm_manager(C);
3054
3055   if (wm->paintcursors.first) {
3056     ARegion *region = CTX_wm_region(C);
3057
3058     if (region) {
3059       wm_paintcursor_tag(C, wm->paintcursors.first, region);
3060     }
3061
3062     /* if previous position was not in current region, we have to set a temp new context */
3063     if (region == NULL || !BLI_rcti_isect_pt_v(&region->winrct, &event->prevx)) {
3064       ScrArea *area = CTX_wm_area(C);
3065
3066       CTX_wm_area_set(C, area_event_inside(C, &event->prevx));
3067       CTX_wm_region_set(C, region_event_inside(C, &event->prevx));
3068
3069       wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
3070
3071       CTX_wm_area_set(C, area);
3072       CTX_wm_region_set(C, region);
3073     }
3074   }
3075 }
3076
3077 static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
3078 {
3079   bScreen *screen = WM_window_get_active_screen(win);
3080
3081   if (BLI_listbase_is_empty(&wm->drags)) {
3082     return;
3083   }
3084
3085   if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
3086     screen->do_draw_drag = true;
3087   }
3088   else if (event->type == EVT_ESCKEY) {
3089     WM_drag_free_list(&wm->drags);
3090
3091     screen->do_draw_drag = true;
3092   }
3093   else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
3094     event->type = EVT_DROP;
3095
3096     /* create customdata, first free existing */
3097     if (event->customdata) {
3098       if (event->customdatafree) {
3099         MEM_freeN(event->customdata);
3100       }
3101     }
3102
3103     event->custom = EVT_DATA_DRAGDROP;
3104     event->customdata = &wm->drags;
3105     event->customdatafree = 1;
3106
3107     /* clear drop icon */
3108     screen->do_draw_drag = true;
3109
3110     /* restore cursor (disabled, see wm_dragdrop.c) */
3111     // WM_cursor_modal_restore(win);
3112   }
3113 }
3114
3115 /* filter out all events of the pie that spawned the last pie unless it's a release event */
3116 static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
3117 {
3118   if (win->lock_pie_event && win->lock_pie_event == event->type) {
3119     if (event->val == KM_RELEASE) {
3120       win->lock_pie_event = EVENT_NONE;
3121       return false;
3122     }
3123     else {
3124       return true;
3125     }
3126   }
3127   else {
3128     return false;
3129   }
3130 }
3131
3132 /**
3133  * Account for the special case when events are being handled and a file is loaded.
3134  * In this case event handling exits early, however when "Load UI" is disabled
3135  * the even will still be in #wmWindow.queue.
3136  *
3137  * Without this it's possible to continuously handle the same event, see: T76484.
3138  */
3139 static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event)
3140 {
3141   LISTBASE_FOREACH (wmWindowManager *, wm, &G_MAIN->wm) {
3142     LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3143       if (BLI_remlink_safe(&win->queue, event)) {
3144         wm_event_free(event);
3145         return;
3146       }
3147     }
3148   }
3149 }
3150
3151 /** \} */
3152
3153 /* -------------------------------------------------------------------- */
3154 /** \name Main Event Queue (Every Window)
3155  *
3156  * Handle events for all windows, run from the #WM_main event loop.
3157  * \{ */
3158
3159 /* called in main loop */
3160 /* goes over entire hierarchy:  events -> window -> screen -> area -> region */
3161 void wm_event_do_handlers(bContext *C)
3162 {
3163   wmWindowManager *wm = CTX_wm_manager(C);
3164   wmWindow *win;
3165
3166   /* update key configuration before handling events */
3167   WM_keyconfig_update(wm);
3168   WM_gizmoconfig_update(CTX_data_main(C));
3169
3170   for (win = wm->windows.first; win; win = win->next) {
3171     bScreen *screen = WM_window_get_active_screen(win);
3172     wmEvent *event;
3173
3174     /* some safety checks - these should always be set! */
3175     BLI_assert(WM_window_get_active_scene(win));
3176     BLI_assert(WM_window_get_active_screen(win));
3177     BLI_assert(WM_window_get_active_workspace(win));
3178
3179     if (screen == NULL) {
3180       wm_event_free_all(win);
3181     }
3182     else {
3183       Main *bmain = CTX_data_main(C);
3184       Scene *scene = WM_window_get_active_scene(win);
3185       ViewLayer *view_layer = WM_window_get_active_view_layer(win);
3186       Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false);
3187       Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL;
3188
3189       if (scene_eval != NULL) {
3190         const int is_playing_sound = BKE_sound_scene_playing(scene_eval);
3191
3192         if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
3193           /* Ignore seek here, the audio will be updated to the scene frame after jump during next
3194            * dependency graph update. */
3195         }
3196         else if (is_playing_sound != -1) {
3197           bool is_playing_screen;
3198
3199           is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
3200
3201           if (((is_playing_sound == 1) && (is_playing_screen == 0)) ||
3202               ((is_playing_sound == 0) && (is_playing_screen == 1))) {
3203             wmWindow *win_ctx = CTX_wm_window(C);
3204             bScreen *screen_stx = CTX_wm_screen(C);
3205             Scene *scene_ctx = CTX_data_scene(C);
3206
3207             CTX_wm_window_set(C, win);
3208             CTX_wm_screen_set(C, screen);
3209             CTX_data_scene_set(C, scene);
3210
3211             ED_screen_animation_play(C, -1, 1);
3212
3213             CTX_data_scene_set(C, scene_ctx);
3214             CTX_wm_screen_set(C, screen_stx);
3215             CTX_wm_window_set(C, win_ctx);
3216           }
3217
3218           if (is_playing_sound == 0) {
3219             const float time = BKE_sound_sync_scene(scene_eval);
3220             if (isfinite(time)) {
3221               int ncfra = time * (float)FPS + 0.5f;
3222               if (ncfra != scene->r.cfra) {
3223                 scene->r.cfra = ncfra;
3224                 ED_update_for_newframe(CTX_data_main(C), depsgraph);
3225                 WM_event_add_notifier(C, NC_WINDOW, NULL);
3226               }
3227             }
3228           }
3229         }
3230       }
3231     }
3232
3233     while ((event = win->queue.first)) {
3234       int action = WM_HANDLER_CONTINUE;
3235
3236       /* active screen might change during handlers, update pointer */
3237       screen = WM_window_get_active_screen(win);
3238
3239       if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) &&
3240           !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3241         printf("\n%s: Handling event\n", __func__);
3242         WM_event_print(event);
3243       }
3244
3245       /* take care of pie event filter */
3246       if (wm_event_pie_filter(win, event)) {
3247         if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3248           CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
3249         }
3250         BLI_remlink(&win->queue, event);
3251         wm_event_free(event);
3252         continue;
3253       }
3254
3255       CTX_wm_window_set(C, win);
3256
3257       /* Clear tool-tip on mouse move. */
3258       if (screen->tool_tip && screen->tool_tip->exit_on_event) {
3259         if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3260           if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, &event->x) > U.move_threshold) {
3261             WM_tooltip_clear(C, win);
3262           }
3263         }
3264       }
3265
3266       /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
3267       CTX_wm_area_set(C, area_event_inside(C, &event->x));
3268       CTX_wm_region_set(C, region_event_inside(C, &event->x));
3269
3270       /* MVC demands to not draw in event handlers...
3271        * but we need to leave it for ogl selecting etc. */
3272       wm_window_make_drawable(wm, win);
3273
3274       wm_region_mouse_co(C, event);
3275
3276       /* first we do priority handlers, modal + some limited keymaps */
3277       action |= wm_handlers_do(C, event, &win->modalhandlers);
3278
3279       /* fileread case */
3280       if (CTX_wm_window(C) == NULL) {
3281         wm_event_free_and_remove_from_queue_if_valid(event);
3282         return;
3283       }
3284
3285       /* check for a tooltip */
3286       if (screen == WM_window_get_active_screen(win)) {
3287         if (screen->tool_tip && screen->tool_tip->timer) {
3288           if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
3289             WM_tooltip_init(C, win);
3290           }
3291         }
3292       }
3293
3294       /* check dragging, creates new event or frees, adds draw tag */
3295       wm_event_drag_and_drop_test(wm, win, event);
3296
3297       /* builtin tweak, if action is break it removes tweak */
3298       wm_tweakevent_test(C, event, action);
3299
3300       if ((action & WM_HANDLER_BREAK) == 0) {
3301         ARegion *region;
3302
3303         /* Note: setting subwin active should be done here, after modal handlers have been done */
3304         if (event->type == MOUSEMOVE) {
3305           /* State variables in screen, cursors.
3306            * Also used in wm_draw.c, fails for modal handlers though. */
3307           ED_screen_set_active_region(C, win, &event->x);
3308           /* for regions having custom cursors */
3309           wm_paintcursor_test(C, event);
3310         }
3311 #ifdef WITH_INPUT_NDOF
3312         else if (event->type == NDOF_MOTION) {
3313           win->addmousemove = true;
3314         }
3315 #endif
3316
3317         ED_screen_areas_iter (win, screen, area) {
3318           /* after restoring a screen from SCREENMAXIMIZED we have to wait
3319            * with the screen handling till the region coordinates are updated */
3320           if (screen->skip_handling == true) {
3321             /* restore for the next iteration of wm_event_do_handlers */
3322             screen->skip_handling = false;
3323             break;
3324           }
3325
3326           /* update azones if needed - done here because it needs to be independent from redraws */
3327           if (area->flag & AREA_FLAG_ACTIONZONES_UPDATE) {
3328             ED_area_azones_update(area, &event->x);
3329           }
3330
3331           if (wm_event_inside_rect(event, &area->totrct)) {
3332             CTX_wm_area_set(C, area);
3333
3334             if ((action & WM_HANDLER_BREAK) == 0) {
3335               for (region = area->regionbase.first; region; region = region->next) {
3336                 if (wm_event_inside_region(event, region)) {
3337
3338                   CTX_wm_region_set(C, region);
3339
3340                   /* call even on non mouse events, since the */
3341                   wm_region_mouse_co(C, event);
3342
3343                   if (!BLI_listbase_is_empty(&wm->drags)) {
3344                     /* does polls for drop regions and checks uibuts */
3345                     /* need to be here to make sure region context is true */
3346                     if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
3347                       wm_drags_check_ops(C, event);
3348                     }
3349                   }
3350
3351                   action |= wm_handlers_do(C, event, &region->handlers);
3352
3353                   /* fileread case (python), [#29489] */
3354                   if (CTX_wm_window(C) == NULL) {
3355                     wm_event_free_and_remove_from_queue_if_valid(event);
3356                     return;
3357                   }
3358
3359                   if (action & WM_HANDLER_BREAK) {
3360                     break;
3361                   }
3362                 }
3363               }
3364             }
3365
3366             CTX_wm_region_set(C, NULL);
3367
3368             if ((action & WM_HANDLER_BREAK) == 0) {
3369               wm_region_mouse_co(C, event); /* only invalidates event->mval in this case */
3370               action |= wm_handlers_do(C, event, &area->handlers);
3371             }
3372             CTX_wm_area_set(C, NULL);
3373
3374             /* NOTE: do not escape on WM_HANDLER_BREAK,
3375              * mousemove needs handled for previous area. */
3376           }
3377         }
3378
3379         if ((action & WM_HANDLER_BREAK) == 0) {
3380           /* also some non-modal handlers need active area/region */
3381           CTX_wm_area_set(C, area_event_inside(C, &event->x));
3382           CTX_wm_region_set(C, region_event_inside(C, &event->x));
3383
3384           wm_region_mouse_co(C, event);
3385
3386           action |= wm_handlers_do(C, event, &win->handlers);
3387
3388           /* fileread case */
3389           if (CTX_wm_window(C) == NULL) {
3390             wm_event_free_and_remove_from_queue_if_valid(event);
3391             return;
3392           }
3393         }
3394       }
3395
3396       /* If press was handled, we don't want to do click. This way
3397        * press in tool keymap can override click in editor keymap.*/
3398       if (ISMOUSE_BUTTON(event->type) && event->val == KM_PRESS &&
3399           !wm_action_not_handled(action)) {
3400         win->eventstate->check_click = false;
3401       }
3402
3403       /* update previous mouse position for following events to use */
3404       win->eventstate->prevx = event->x;
3405       win->eventstate->prevy = event->y;
3406
3407       /* unlink and free here, blender-quit then frees all */
3408       BLI_remlink(&win->queue, event);
3409       wm_event_free(event);
3410     }
3411
3412     /* only add mousemove when queue was read entirely */
3413     if (win->addmousemove && win->eventstate) {
3414       wmEvent tevent = *(win->eventstate);
3415       // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
3416       tevent.type = MOUSEMOVE;
3417       tevent.prevx = tevent.x;
3418       tevent.prevy = tevent.y;
3419       wm_event_add(win, &tevent);
3420       win->addmousemove = 0;
3421     }
3422
3423     CTX_wm_window_set(C, NULL);
3424   }
3425
3426   /* update key configuration after handling events */
3427   WM_keyconfig_update(wm);
3428   WM_gizmoconfig_update(CTX_data_main(C));
3429 }
3430
3431 /** \} */
3432
3433 /* -------------------------------------------------------------------- */
3434 /** \name File Selector Handling
3435  * \{ */
3436
3437 void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval)
3438 {
3439   /* add to all windows! */
3440   wmWindow *win;
3441
3442   for (win = wm->windows.first; win; win = win->next) {
3443     wmEvent event = *win->eventstate;
3444
3445     event.type = EVT_FILESELECT;
3446     event.val = eventval;
3447     event.customdata = ophandle;  // only as void pointer type check
3448
3449     wm_event_add(win, &event);
3450   }
3451 }
3452
3453 /* operator is supposed to have a filled "path" property */
3454 /* optional property: filetype (XXX enum?) */
3455
3456 /**
3457  * The idea here is to keep a handler alive on window queue, owning the operator.
3458  * The file window can send event to make it execute, thus ensuring
3459  * executing happens outside of lower level queues, with UI refreshed.
3460  * Should also allow multiwin solutions
3461  */
3462 void WM_event_add_fileselect(bContext *C, wmOperator *op)
3463 {
3464   wmWindowManager *wm = CTX_wm_manager(C);
3465   wmWindow *win = CTX_wm_window(C);
3466   const bool is_temp_screen = WM_window_is_temp_screen(win);
3467   const bool opens_window = (U.filebrowser_display_type == USER_TEMP_SPACE_DISPLAY_WINDOW);
3468   /* Don't add the file handler to the temporary window if one is opened, or else it owns the
3469    * handlers for itself, causing dangling pointers once it's destructed through a handler. It has
3470    * a parent which should hold the handlers itself. */
3471   ListBase *modalhandlers = (is_temp_screen && opens_window) ? &win->parent->modalhandlers :
3472                                                                &win->modalhandlers;
3473
3474   /* Close any popups, like when opening a file browser from the splash. */
3475   UI_popup_handlers_remove_all(C, modalhandlers);
3476
3477   if (!is_temp_screen) {
3478     /* only allow 1 file selector open per window */
3479     LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, modalhandlers) {
3480       if (handler_base->type == WM_HANDLER_TYPE_OP) {
3481         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3482         if (handler->is_fileselect == false) {
3483           continue;
3484         }
3485         bScreen *screen = CTX_wm_screen(C);
3486         bool cancel_handler = true;
3487
3488         /* find the area with the file selector for this handler */
3489         ED_screen_areas_iter (win, screen, area) {
3490           if (area->spacetype == SPACE_FILE) {
3491             SpaceFile *sfile = area->spacedata.first;
3492
3493             if (sfile->op == handler->op) {
3494               CTX_wm_area_set(C, area);
3495