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 <math.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "DNA_listBase.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_userdef_types.h"
36 #include "DNA_windowmanager_types.h"
37 
38 #include "MEM_guardedalloc.h"
39 
40 #include "CLG_log.h"
41 
42 #include "GHOST_C-api.h"
43 
44 #include "BLI_blenlib.h"
45 #include "BLI_dynstr.h"
46 #include "BLI_math.h"
47 #include "BLI_timer.h"
48 #include "BLI_utildefines.h"
49 
50 #include "BKE_context.h"
51 #include "BKE_customdata.h"
52 #include "BKE_global.h"
53 #include "BKE_idprop.h"
54 #include "BKE_main.h"
55 #include "BKE_report.h"
56 #include "BKE_scene.h"
57 #include "BKE_screen.h"
58 #include "BKE_workspace.h"
59 
60 #include "BKE_sound.h"
61 
62 #include "BLT_translation.h"
63 
64 #include "ED_fileselect.h"
65 #include "ED_info.h"
66 #include "ED_screen.h"
67 #include "ED_undo.h"
68 #include "ED_util.h"
69 #include "ED_view3d.h"
70 
71 #include "RNA_access.h"
72 
73 #include "UI_interface.h"
74 
75 #include "PIL_time.h"
76 
77 #include "WM_api.h"
78 #include "WM_message.h"
79 #include "WM_toolsystem.h"
80 #include "WM_types.h"
81 
82 #include "wm.h"
83 #include "wm_event_system.h"
84 #include "wm_event_types.h"
85 #include "wm_window.h"
86 
87 #include "DEG_depsgraph.h"
88 #include "DEG_depsgraph_query.h"
89 
90 /**
91  * When a gizmo is highlighted and uses click/drag events,
92  * this prevents mouse button press events from being passed through to other key-maps
93  * which would obscure those events.
94  *
95  * This allows gizmos that only use drag to co-exist with tools that use click.
96  *
97  * Without tools using press events which would prevent click/drag events getting to the gizmos.
98  *
99  * This is not a fool proof solution since since it's possible the gizmo operators would pass
100  * through these events when called, see: T65479.
101  */
102 #define USE_GIZMO_MOUSE_PRIORITY_HACK
103 
104 static void wm_notifier_clear(wmNotifier *note);
105 
106 static int wm_operator_call_internal(bContext *C,
107                                      wmOperatorType *ot,
108                                      PointerRNA *properties,
109                                      ReportList *reports,
110                                      const short context,
111                                      const bool poll_only,
112                                      wmEvent *event);
113 
114 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot);
115 
116 /* -------------------------------------------------------------------- */
117 /** \name Event Management
118  * \{ */
119 
wm_event_add_ex(wmWindow * win,const wmEvent * event_to_add,const wmEvent * event_to_add_after)120 wmEvent *wm_event_add_ex(wmWindow *win,
121                          const wmEvent *event_to_add,
122                          const wmEvent *event_to_add_after)
123 {
124   wmEvent *event = MEM_mallocN(sizeof(wmEvent), "wmEvent");
125 
126   *event = *event_to_add;
127 
128   if (event_to_add_after == NULL) {
129     BLI_addtail(&win->queue, event);
130   }
131   else {
132     /* Note: strictly speaking this breaks const-correctness,
133      * however we're only changing 'next' member. */
134     BLI_insertlinkafter(&win->queue, (void *)event_to_add_after, event);
135   }
136   return event;
137 }
138 
wm_event_add(wmWindow * win,const wmEvent * event_to_add)139 wmEvent *wm_event_add(wmWindow *win, const wmEvent *event_to_add)
140 {
141   return wm_event_add_ex(win, event_to_add, NULL);
142 }
143 
WM_event_add_simulate(wmWindow * win,const wmEvent * event_to_add)144 wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
145 {
146   if ((G.f & G_FLAG_EVENT_SIMULATE) == 0) {
147     BLI_assert(0);
148     return NULL;
149   }
150   wmEvent *event = wm_event_add(win, event_to_add);
151   win->eventstate->x = event->x;
152   win->eventstate->y = event->y;
153   return event;
154 }
155 
wm_event_free(wmEvent * event)156 void wm_event_free(wmEvent *event)
157 {
158 #ifndef NDEBUG
159   /* Don't use assert here because it's fairly harmless in most cases,
160    * more an issue of correctness, something we should avoid in general. */
161   if (event->is_repeat && !ISKEYBOARD(event->type)) {
162     printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__);
163     WM_event_print(event);
164   }
165 #endif
166 
167   if (event->customdata) {
168     if (event->customdatafree) {
169       /* Note: pointer to listbase struct elsewhere. */
170       if (event->custom == EVT_DATA_DRAGDROP) {
171         ListBase *lb = event->customdata;
172         WM_drag_free_list(lb);
173       }
174       else {
175         MEM_freeN(event->customdata);
176       }
177     }
178   }
179 
180   MEM_freeN(event);
181 }
182 
wm_event_free_all(wmWindow * win)183 void wm_event_free_all(wmWindow *win)
184 {
185   wmEvent *event;
186   while ((event = BLI_pophead(&win->queue))) {
187     wm_event_free(event);
188   }
189 }
190 
wm_event_init_from_window(wmWindow * win,wmEvent * event)191 void wm_event_init_from_window(wmWindow *win, wmEvent *event)
192 {
193   *event = *(win->eventstate);
194 }
195 
196 /** \} */
197 
198 /* -------------------------------------------------------------------- */
199 /** \name Notifiers & Listeners
200  * \{ */
201 
wm_test_duplicate_notifier(const wmWindowManager * wm,uint type,void * reference)202 static bool wm_test_duplicate_notifier(const wmWindowManager *wm, uint type, void *reference)
203 {
204   LISTBASE_FOREACH (wmNotifier *, note, &wm->queue) {
205     if ((note->category | note->data | note->subtype | note->action) == type &&
206         note->reference == reference) {
207       return 1;
208     }
209   }
210 
211   return 0;
212 }
213 
WM_event_add_notifier_ex(wmWindowManager * wm,const wmWindow * win,uint type,void * reference)214 void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
215 {
216   if (wm_test_duplicate_notifier(wm, type, reference)) {
217     return;
218   }
219 
220   wmNotifier *note = MEM_callocN(sizeof(wmNotifier), "notifier");
221 
222   BLI_addtail(&wm->queue, note);
223 
224   note->window = win;
225 
226   note->category = type & NOTE_CATEGORY;
227   note->data = type & NOTE_DATA;
228   note->subtype = type & NOTE_SUBTYPE;
229   note->action = type & NOTE_ACTION;
230 
231   note->reference = reference;
232 }
233 
234 /* XXX: in future, which notifiers to send to other windows? */
WM_event_add_notifier(const bContext * C,uint type,void * reference)235 void WM_event_add_notifier(const bContext *C, uint type, void *reference)
236 {
237   WM_event_add_notifier_ex(CTX_wm_manager(C), CTX_wm_window(C), type, reference);
238 }
239 
WM_main_add_notifier(unsigned int type,void * reference)240 void WM_main_add_notifier(unsigned int type, void *reference)
241 {
242   Main *bmain = G_MAIN;
243   wmWindowManager *wm = bmain->wm.first;
244 
245   if (!wm || wm_test_duplicate_notifier(wm, type, reference)) {
246     return;
247   }
248 
249   wmNotifier *note = MEM_callocN(sizeof(wmNotifier), "notifier");
250 
251   BLI_addtail(&wm->queue, note);
252 
253   note->category = type & NOTE_CATEGORY;
254   note->data = type & NOTE_DATA;
255   note->subtype = type & NOTE_SUBTYPE;
256   note->action = type & NOTE_ACTION;
257 
258   note->reference = reference;
259 }
260 
261 /**
262  * Clear notifiers by reference, Used so listeners don't act on freed data.
263  */
WM_main_remove_notifier_reference(const void * reference)264 void WM_main_remove_notifier_reference(const void *reference)
265 {
266   Main *bmain = G_MAIN;
267   wmWindowManager *wm = bmain->wm.first;
268 
269   if (wm) {
270     LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->queue) {
271       if (note->reference == reference) {
272         /* Don't remove because this causes problems for #wm_event_do_notifiers
273          * which may be looping on the data (deleting screens). */
274         wm_notifier_clear(note);
275       }
276     }
277 
278     /* Remap instead. */
279 #if 0
280     if (wm->message_bus) {
281       WM_msg_id_remove(wm->message_bus, reference);
282     }
283 #endif
284   }
285 }
286 
WM_main_remap_editor_id_reference(ID * old_id,ID * new_id)287 void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
288 {
289   Main *bmain = G_MAIN;
290 
291   LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
292     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
293       LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
294         ED_spacedata_id_remap(area, sl, old_id, new_id);
295       }
296     }
297   }
298 
299   wmWindowManager *wm = bmain->wm.first;
300   if (wm && wm->message_bus) {
301     struct wmMsgBus *mbus = wm->message_bus;
302     if (new_id != NULL) {
303       WM_msg_id_update(mbus, old_id, new_id);
304     }
305     else {
306       WM_msg_id_remove(mbus, old_id);
307     }
308   }
309 }
310 
wm_notifier_clear(wmNotifier * note)311 static void wm_notifier_clear(wmNotifier *note)
312 {
313   /* NULL the entire notifier, only leaving (next, prev) members intact. */
314   memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
315 }
316 
wm_event_do_depsgraph(bContext * C,bool is_after_open_file)317 void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
318 {
319   wmWindowManager *wm = CTX_wm_manager(C);
320   /* The whole idea of locked interface is to prevent viewport and whatever
321    * thread from modifying the same data. Because of this, we can not perform
322    * dependency graph update.
323    */
324   if (wm->is_interface_locked) {
325     return;
326   }
327   /* Combine datamasks so one window doesn't disable UV's in another T26448. */
328   CustomData_MeshMasks win_combine_v3d_datamask = {0};
329   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
330     const Scene *scene = WM_window_get_active_scene(win);
331     const bScreen *screen = WM_window_get_active_screen(win);
332 
333     ED_view3d_screen_datamask(C, scene, screen, &win_combine_v3d_datamask);
334   }
335   /* Update all the dependency graphs of visible view layers. */
336   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
337     Scene *scene = WM_window_get_active_scene(win);
338     ViewLayer *view_layer = WM_window_get_active_view_layer(win);
339     Main *bmain = CTX_data_main(C);
340     /* Copied to set's in scene_update_tagged_recursive() */
341     scene->customdata_mask = win_combine_v3d_datamask;
342     /* XXX, hack so operators can enforce datamasks T26482, gl render */
343     CustomData_MeshMasks_update(&scene->customdata_mask, &scene->customdata_mask_modal);
344     /* TODO(sergey): For now all dependency graphs which are evaluated from
345      * workspace are considered active. This will work all fine with "locked"
346      * view layer and time across windows. This is to be granted separately,
347      * and for until then we have to accept ambiguities when object is shared
348      * across visible view layers and has overrides on it.
349      */
350     Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
351     if (is_after_open_file) {
352       DEG_graph_relations_update(depsgraph);
353       DEG_graph_on_visible_update(bmain, depsgraph, true);
354     }
355     DEG_make_active(depsgraph);
356     BKE_scene_graph_update_tagged(depsgraph, bmain);
357   }
358 }
359 
360 /**
361  * Was part of #wm_event_do_notifiers,
362  * split out so it can be called once before entering the #WM_main loop.
363  * This ensures operators don't run before the UI and depsgraph are initialized.
364  */
wm_event_do_refresh_wm_and_depsgraph(bContext * C)365 void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
366 {
367   wmWindowManager *wm = CTX_wm_manager(C);
368   /* Cached: editor refresh callbacks now, they get context. */
369   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
370     const bScreen *screen = WM_window_get_active_screen(win);
371     ScrArea *area;
372 
373     CTX_wm_window_set(C, win);
374     for (area = screen->areabase.first; area; area = area->next) {
375       if (area->do_refresh) {
376         CTX_wm_area_set(C, area);
377         ED_area_do_refresh(C, area);
378       }
379     }
380   }
381 
382   wm_event_do_depsgraph(C, false);
383 
384   CTX_wm_window_set(C, NULL);
385 }
386 
wm_event_execute_timers(bContext * C)387 static void wm_event_execute_timers(bContext *C)
388 {
389   wmWindowManager *wm = CTX_wm_manager(C);
390   if (UNLIKELY(wm == NULL)) {
391     return;
392   }
393 
394   /* Set the first window as context, so that there is some minimal context. This avoids crashes
395    * when calling code that assumes that there is always a window in the context (which many
396    * operators do). */
397   CTX_wm_window_set(C, wm->windows.first);
398   BLI_timer_execute();
399   CTX_wm_window_set(C, NULL);
400 }
401 
402 /* Called in mainloop. */
wm_event_do_notifiers(bContext * C)403 void wm_event_do_notifiers(bContext *C)
404 {
405   /* Run the timer before assigning 'wm' in the unlikely case a timer loads a file, see T80028. */
406   wm_event_execute_timers(C);
407 
408   wmWindowManager *wm = CTX_wm_manager(C);
409   if (wm == NULL) {
410     return;
411   }
412 
413   /* Disable? - Keep for now since its used for window level notifiers. */
414 #if 1
415   /* Cache & catch WM level notifiers, such as frame change, scene/screen set. */
416   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
417     Scene *scene = WM_window_get_active_scene(win);
418     bool do_anim = false;
419 
420     CTX_wm_window_set(C, win);
421 
422     LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->queue) {
423       if (note->category == NC_WM) {
424         if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
425           wm->file_saved = 1;
426           wm_window_title(wm, win);
427         }
428         else if (note->data == ND_DATACHANGED) {
429           wm_window_title(wm, win);
430         }
431       }
432       if (note->window == win) {
433         if (note->category == NC_SCREEN) {
434           if (note->data == ND_WORKSPACE_SET) {
435             WorkSpace *ref_ws = note->reference;
436 
437             UI_popup_handlers_remove_all(C, &win->modalhandlers);
438 
439             WM_window_set_active_workspace(C, win, ref_ws);
440             if (G.debug & G_DEBUG_EVENTS) {
441               printf("%s: Workspace set %p\n", __func__, note->reference);
442             }
443           }
444           else if (note->data == ND_WORKSPACE_DELETE) {
445             WorkSpace *workspace = note->reference;
446 
447             ED_workspace_delete(
448                 workspace, CTX_data_main(C), C, wm); /* XXX hrms, think this over! */
449             if (G.debug & G_DEBUG_EVENTS) {
450               printf("%s: Workspace delete %p\n", __func__, workspace);
451             }
452           }
453           else if (note->data == ND_LAYOUTBROWSE) {
454             bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
455 
456             /* free popup handlers only T35434. */
457             UI_popup_handlers_remove_all(C, &win->modalhandlers);
458 
459             ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
460             if (G.debug & G_DEBUG_EVENTS) {
461               printf("%s: screen set %p\n", __func__, note->reference);
462             }
463           }
464           else if (note->data == ND_LAYOUTDELETE) {
465             WorkSpace *workspace = WM_window_get_active_workspace(win);
466             WorkSpaceLayout *layout = note->reference;
467 
468             ED_workspace_layout_delete(workspace, layout, C); /* XXX hrms, think this over! */
469             if (G.debug & G_DEBUG_EVENTS) {
470               printf("%s: screen delete %p\n", __func__, note->reference);
471             }
472           }
473         }
474       }
475 
476       if (note->window == win ||
477           (note->window == NULL && (note->reference == NULL || note->reference == scene))) {
478         if (note->category == NC_SCENE) {
479           if (note->data == ND_FRAME) {
480             do_anim = true;
481           }
482         }
483       }
484       if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
485         ViewLayer *view_layer = CTX_data_view_layer(C);
486         ED_info_stats_clear(view_layer);
487         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
488       }
489     }
490     if (do_anim) {
491 
492       /* XXX, quick frame changes can cause a crash if framechange and rendering
493        * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
494        * twice which can depgraph update the same object at once */
495       if (G.is_rendering == false) {
496         /* Depsgraph gets called, might send more notifiers. */
497         Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
498         ED_update_for_newframe(CTX_data_main(C), depsgraph);
499       }
500     }
501   }
502 
503   /* The notifiers are sent without context, to keep it clean. */
504   wmNotifier *note;
505   while ((note = BLI_pophead(&wm->queue))) {
506     LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
507       Scene *scene = WM_window_get_active_scene(win);
508       bScreen *screen = WM_window_get_active_screen(win);
509       WorkSpace *workspace = WM_window_get_active_workspace(win);
510 
511       /* Dilter out notifiers. */
512       if (note->category == NC_SCREEN && note->reference && note->reference != screen &&
513           note->reference != workspace && note->reference != WM_window_get_active_layout(win)) {
514         /* Pass. */
515       }
516       else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
517         /* Pass. */
518       }
519       else {
520         ARegion *region;
521 
522         /* XXX context in notifiers? */
523         CTX_wm_window_set(C, win);
524 
525 #  if 0
526         printf("notifier win %d screen %s cat %x\n",
527                win->winid,
528                win->screen->id.name + 2,
529                note->category);
530 #  endif
531         ED_screen_do_listen(C, note);
532 
533         for (region = screen->regionbase.first; region; region = region->next) {
534           ED_region_do_listen(win, NULL, region, note, scene);
535         }
536 
537         ED_screen_areas_iter (win, screen, area) {
538           ED_area_do_listen(win, area, note, scene);
539           for (region = area->regionbase.first; region; region = region->next) {
540             ED_region_do_listen(win, area, region, note, scene);
541           }
542         }
543       }
544     }
545 
546     MEM_freeN(note);
547   }
548 #endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */
549 
550   /* Handle message bus. */
551   {
552     LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
553       CTX_wm_window_set(C, win);
554       WM_msgbus_handle(wm->message_bus, C);
555     }
556     CTX_wm_window_set(C, NULL);
557   }
558 
559   wm_event_do_refresh_wm_and_depsgraph(C);
560 
561   /* Status bar */
562   if (wm->winactive) {
563     wmWindow *win = wm->winactive;
564     CTX_wm_window_set(C, win);
565     WM_window_cursor_keymap_status_refresh(C, win);
566     CTX_wm_window_set(C, NULL);
567   }
568 
569   /* Autorun warning */
570   wm_test_autorun_warning(C);
571 }
572 
wm_event_always_pass(const wmEvent * event)573 static int wm_event_always_pass(const wmEvent *event)
574 {
575   /* Some events we always pass on, to ensure proper communication. */
576   return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
577 }
578 
579 /** \} */
580 
581 /* -------------------------------------------------------------------- */
582 /** \name UI Handling
583  * \{ */
584 
wm_handler_ui_call(bContext * C,wmEventHandler_UI * handler,const wmEvent * event,int always_pass)585 static int wm_handler_ui_call(bContext *C,
586                               wmEventHandler_UI *handler,
587                               const wmEvent *event,
588                               int always_pass)
589 {
590   ScrArea *area = CTX_wm_area(C);
591   ARegion *region = CTX_wm_region(C);
592   ARegion *menu = CTX_wm_menu(C);
593   static bool do_wheel_ui = true;
594   const bool is_wheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN);
595 
596   /* UI code doesn't handle return values - it just always returns break.
597    * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks. */
598   if (((handler->head.flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) && !ISMOUSE_BUTTON(event->type) &&
599       (event->val == KM_DBL_CLICK)) {
600     return WM_HANDLER_CONTINUE;
601   }
602 
603   /* UI is quite aggressive with swallowing events, like scroll-wheel. */
604   /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter. */
605   if (do_wheel_ui == false) {
606     if (is_wheel) {
607       return WM_HANDLER_CONTINUE;
608     }
609     if (wm_event_always_pass(event) == 0) {
610       do_wheel_ui = true;
611     }
612   }
613 
614   /* Don't block file-select events. Those are triggered by a separate file browser window.
615    * See T75292. */
616   if (event->type == EVT_FILESELECT) {
617     return WM_UI_HANDLER_CONTINUE;
618   }
619 
620   /* We set context to where UI handler came from. */
621   if (handler->context.area) {
622     CTX_wm_area_set(C, handler->context.area);
623   }
624   if (handler->context.region) {
625     CTX_wm_region_set(C, handler->context.region);
626   }
627   if (handler->context.menu) {
628     CTX_wm_menu_set(C, handler->context.menu);
629   }
630 
631   int retval = handler->handle_fn(C, event, handler->user_data);
632 
633   /* putting back screen context */
634   if ((retval != WM_UI_HANDLER_BREAK) || always_pass) {
635     CTX_wm_area_set(C, area);
636     CTX_wm_region_set(C, region);
637     CTX_wm_menu_set(C, menu);
638   }
639   else {
640     /* This special cases is for areas and regions that get removed. */
641     CTX_wm_area_set(C, NULL);
642     CTX_wm_region_set(C, NULL);
643     CTX_wm_menu_set(C, NULL);
644   }
645 
646   if (retval == WM_UI_HANDLER_BREAK) {
647     return WM_HANDLER_BREAK;
648   }
649 
650   /* event not handled in UI, if wheel then we temporarily disable it */
651   if (is_wheel) {
652     do_wheel_ui = false;
653   }
654 
655   return WM_HANDLER_CONTINUE;
656 }
657 
wm_event_handler_ui_cancel_ex(bContext * C,wmWindow * win,ARegion * region,bool reactivate_button)658 void wm_event_handler_ui_cancel_ex(bContext *C,
659                                    wmWindow *win,
660                                    ARegion *region,
661                                    bool reactivate_button)
662 {
663   if (!region) {
664     return;
665   }
666 
667   LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &region->handlers) {
668     if (handler_base->type == WM_HANDLER_TYPE_UI) {
669       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
670       BLI_assert(handler->handle_fn != NULL);
671       wmEvent event;
672       wm_event_init_from_window(win, &event);
673       event.type = EVT_BUT_CANCEL;
674       event.val = reactivate_button ? 0 : 1;
675       event.is_repeat = false;
676       handler->handle_fn(C, &event, handler->user_data);
677     }
678   }
679 }
680 
wm_event_handler_ui_cancel(bContext * C)681 static void wm_event_handler_ui_cancel(bContext *C)
682 {
683   wmWindow *win = CTX_wm_window(C);
684   ARegion *region = CTX_wm_region(C);
685   wm_event_handler_ui_cancel_ex(C, win, region, true);
686 }
687 
688 /** \} */
689 
690 /* -------------------------------------------------------------------- */
691 /** \name WM Reports
692  *
693  * Access to #wmWindowManager.reports
694  * \{ */
695 
696 /**
697  * Show the report in the info header.
698  */
WM_report_banner_show(void)699 void WM_report_banner_show(void)
700 {
701   wmWindowManager *wm = G_MAIN->wm.first;
702   ReportList *wm_reports = &wm->reports;
703 
704   /* After adding reports to the global list, reset the report timer. */
705   WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
706 
707   /* Records time since last report was added */
708   wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05);
709 
710   ReportTimerInfo *rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
711   wm_reports->reporttimer->customdata = rti;
712 }
713 
714 /**
715  * Hide all currently displayed banners and abort their timer.
716  */
WM_report_banners_cancel(Main * bmain)717 void WM_report_banners_cancel(Main *bmain)
718 {
719   wmWindowManager *wm = bmain->wm.first;
720   BKE_reports_clear(&wm->reports);
721   WM_event_remove_timer(wm, NULL, wm->reports.reporttimer);
722 }
723 
724 #ifdef WITH_INPUT_NDOF
WM_ndof_deadzone_set(float deadzone)725 void WM_ndof_deadzone_set(float deadzone)
726 {
727   GHOST_setNDOFDeadZone(deadzone);
728 }
729 #endif
730 
wm_add_reports(ReportList * reports)731 static void wm_add_reports(ReportList *reports)
732 {
733   /* If the caller owns them, handle this. */
734   if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) {
735     wmWindowManager *wm = G_MAIN->wm.first;
736 
737     /* add reports to the global list, otherwise they are not seen */
738     BLI_movelisttolist(&wm->reports.list, &reports->list);
739 
740     WM_report_banner_show();
741   }
742 }
743 
WM_report(ReportType type,const char * message)744 void WM_report(ReportType type, const char *message)
745 {
746   ReportList reports;
747   BKE_reports_init(&reports, RPT_STORE);
748   BKE_report(&reports, type, message);
749 
750   wm_add_reports(&reports);
751 
752   BKE_reports_clear(&reports);
753 }
754 
WM_reportf(ReportType type,const char * format,...)755 void WM_reportf(ReportType type, const char *format, ...)
756 {
757   va_list args;
758 
759   DynStr *ds = BLI_dynstr_new();
760   va_start(args, format);
761   BLI_dynstr_vappendf(ds, format, args);
762   va_end(args);
763 
764   char *str = BLI_dynstr_get_cstring(ds);
765   WM_report(type, str);
766   MEM_freeN(str);
767 
768   BLI_dynstr_free(ds);
769 }
770 
771 /** \} */
772 
773 /* -------------------------------------------------------------------- */
774 /** \name Operator Logic
775  * \{ */
776 
WM_operator_poll(bContext * C,wmOperatorType * ot)777 bool WM_operator_poll(bContext *C, wmOperatorType *ot)
778 {
779 
780   LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) {
781     wmOperatorType *ot_macro = WM_operatortype_find(macro->idname, 0);
782 
783     if (0 == WM_operator_poll(C, ot_macro)) {
784       return 0;
785     }
786   }
787 
788   /* Python needs operator type, so we added exception for it. */
789   if (ot->pyop_poll) {
790     return ot->pyop_poll(C, ot);
791   }
792   if (ot->poll) {
793     return ot->poll(C);
794   }
795 
796   return 1;
797 }
798 
799 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
WM_operator_poll_context(bContext * C,wmOperatorType * ot,short context)800 bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
801 {
802   return wm_operator_call_internal(C, ot, NULL, NULL, context, true, NULL);
803 }
804 
WM_operator_check_ui_empty(wmOperatorType * ot)805 bool WM_operator_check_ui_empty(wmOperatorType *ot)
806 {
807   if (ot->macro.first != NULL) {
808     /* For macros, check all have exec() we can call. */
809     LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &ot->macro) {
810       wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
811       if (otm && !WM_operator_check_ui_empty(otm)) {
812         return false;
813       }
814     }
815     return true;
816   }
817 
818   /* Assume a UI callback will draw something. */
819   if (ot->ui) {
820     return false;
821   }
822 
823   PointerRNA ptr;
824   WM_operator_properties_create_ptr(&ptr, ot);
825   RNA_STRUCT_BEGIN (&ptr, prop) {
826     int flag = RNA_property_flag(prop);
827     if (flag & PROP_HIDDEN) {
828       continue;
829     }
830     return false;
831   }
832   RNA_STRUCT_END;
833   return true;
834 }
835 
836 /**
837  * Sets the active region for this space from the context.
838  *
839  * \see #BKE_area_find_region_active_win
840  */
WM_operator_region_active_win_set(bContext * C)841 void WM_operator_region_active_win_set(bContext *C)
842 {
843   ScrArea *area = CTX_wm_area(C);
844   if (area) {
845     ARegion *region = CTX_wm_region(C);
846     if (region && region->regiontype == RGN_TYPE_WINDOW) {
847       area->region_active_win = BLI_findindex(&area->regionbase, region);
848     }
849   }
850 }
851 
852 /* (caller_owns_reports == true) when called from python. */
wm_operator_reports(bContext * C,wmOperator * op,int retval,bool caller_owns_reports)853 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports)
854 {
855   if (G.background == 0 && caller_owns_reports == false) { /* popup */
856     if (op->reports->list.first) {
857       /* FIXME, temp setting window, see other call to UI_popup_menu_reports for why. */
858       wmWindow *win_prev = CTX_wm_window(C);
859       ScrArea *area_prev = CTX_wm_area(C);
860       ARegion *region_prev = CTX_wm_region(C);
861 
862       if (win_prev == NULL) {
863         CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
864       }
865 
866       UI_popup_menu_reports(C, op->reports);
867 
868       CTX_wm_window_set(C, win_prev);
869       CTX_wm_area_set(C, area_prev);
870       CTX_wm_region_set(C, region_prev);
871     }
872   }
873 
874   if (retval & OPERATOR_FINISHED) {
875     CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
876 
877     if (caller_owns_reports == false) {
878       BKE_reports_print(op->reports, RPT_DEBUG); /* Print out reports to console. */
879     }
880 
881     if (op->type->flag & OPTYPE_REGISTER) {
882       if (G.background == 0) { /* Ends up printing these in the terminal, gets annoying. */
883         /* Report the python string representation of the operator. */
884         char *buf = WM_operator_pystring(C, op, false, true);
885         BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
886         MEM_freeN(buf);
887       }
888     }
889   }
890 
891   /* Refresh Info Editor with reports immediately, even if op returned OPERATOR_CANCELLED. */
892   if ((retval & OPERATOR_CANCELLED) && !BLI_listbase_is_empty(&op->reports->list)) {
893     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
894   }
895   /* If the caller owns them, handle this. */
896   wm_add_reports(op->reports);
897 }
898 
899 /**
900  * This function is mainly to check that the rules for freeing
901  * an operator are kept in sync.
902  */
wm_operator_register_check(wmWindowManager * wm,wmOperatorType * ot)903 static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
904 {
905   /* Check undo flag here since undo operators are also added to the list,
906    * to support checking if the same operator is run twice. */
907   return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
908 }
909 
wm_operator_finished(bContext * C,wmOperator * op,const bool repeat,const bool store)910 static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
911 {
912   wmWindowManager *wm = CTX_wm_manager(C);
913   enum {
914     NOP,
915     SET,
916     CLEAR,
917   } hud_status = NOP;
918 
919   op->customdata = NULL;
920 
921   if (store) {
922     WM_operator_last_properties_store(op);
923   }
924 
925   /* We don't want to do undo pushes for operators that are being
926    * called from operators that already do an undo push. Usually
927    * this will happen for python operators that call C operators. */
928   if (wm->op_undo_depth == 0) {
929     if (op->type->flag & OPTYPE_UNDO) {
930       ED_undo_push_op(C, op);
931       if (repeat == 0) {
932         hud_status = CLEAR;
933       }
934     }
935     else if (op->type->flag & OPTYPE_UNDO_GROUPED) {
936       ED_undo_grouped_push_op(C, op);
937       if (repeat == 0) {
938         hud_status = CLEAR;
939       }
940     }
941   }
942 
943   if (repeat == 0) {
944     if (G.debug & G_DEBUG_WM) {
945       char *buf = WM_operator_pystring(C, op, false, true);
946       BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
947       MEM_freeN(buf);
948     }
949 
950     if (wm_operator_register_check(wm, op->type)) {
951       /* take ownership of reports (in case python provided own) */
952       op->reports->flag |= RPT_FREE;
953 
954       wm_operator_register(C, op);
955       WM_operator_region_active_win_set(C);
956 
957       if (WM_operator_last_redo(C) == op) {
958         /* Show the redo panel. */
959         hud_status = SET;
960       }
961     }
962     else {
963       WM_operator_free(op);
964     }
965   }
966 
967   if (hud_status != NOP) {
968     if (hud_status == SET) {
969       ScrArea *area = CTX_wm_area(C);
970       if (area) {
971         ED_area_type_hud_ensure(C, area);
972       }
973     }
974     else if (hud_status == CLEAR) {
975       ED_area_type_hud_clear(wm, NULL);
976     }
977     else {
978       BLI_assert(0);
979     }
980   }
981 }
982 
983 /* If repeat is true, it doesn't register again, nor does it free. */
wm_operator_exec(bContext * C,wmOperator * op,const bool repeat,const bool store)984 static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, const bool store)
985 {
986   wmWindowManager *wm = CTX_wm_manager(C);
987   int retval = OPERATOR_CANCELLED;
988 
989   CTX_wm_operator_poll_msg_set(C, NULL);
990 
991   if (op == NULL || op->type == NULL) {
992     return retval;
993   }
994 
995   if (0 == WM_operator_poll(C, op->type)) {
996     return retval;
997   }
998 
999   if (op->type->exec) {
1000     if (op->type->flag & OPTYPE_UNDO) {
1001       wm->op_undo_depth++;
1002     }
1003 
1004     retval = op->type->exec(C, op);
1005     OPERATOR_RETVAL_CHECK(retval);
1006 
1007     if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1008       wm->op_undo_depth--;
1009     }
1010   }
1011 
1012   /* XXX(mont29) Disabled the repeat check to address part 2 of T31840.
1013    * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
1014    * why this was needed, but worth to note it in case something turns bad. */
1015   if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
1016     wm_operator_reports(C, op, retval, false);
1017   }
1018 
1019   if (retval & OPERATOR_FINISHED) {
1020     wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
1021   }
1022   else if (repeat == 0) {
1023     /* warning: modal from exec is bad practice, but avoid crashing. */
1024     if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
1025       WM_operator_free(op);
1026     }
1027   }
1028 
1029   return retval | OPERATOR_HANDLED;
1030 }
1031 
1032 /* Simply calls exec with basic checks. */
wm_operator_exec_notest(bContext * C,wmOperator * op)1033 static int wm_operator_exec_notest(bContext *C, wmOperator *op)
1034 {
1035   int retval = OPERATOR_CANCELLED;
1036 
1037   if (op == NULL || op->type == NULL || op->type->exec == NULL) {
1038     return retval;
1039   }
1040 
1041   retval = op->type->exec(C, op);
1042   OPERATOR_RETVAL_CHECK(retval);
1043 
1044   return retval;
1045 }
1046 
1047 /**
1048  * For running operators with frozen context (modal handlers, menus).
1049  *
1050  * \param store: Store settings for re-use.
1051  *
1052  * \warning do not use this within an operator to call its self! T29537.
1053  */
WM_operator_call_ex(bContext * C,wmOperator * op,const bool store)1054 int WM_operator_call_ex(bContext *C, wmOperator *op, const bool store)
1055 {
1056   return wm_operator_exec(C, op, false, store);
1057 }
1058 
WM_operator_call(bContext * C,wmOperator * op)1059 int WM_operator_call(bContext *C, wmOperator *op)
1060 {
1061   return WM_operator_call_ex(C, op, false);
1062 }
1063 
1064 /**
1065  * This is intended to be used when an invoke operator wants to call exec on its self
1066  * and is basically like running op->type->exec() directly, no poll checks no freeing,
1067  * since we assume whoever called invoke will take care of that
1068  */
WM_operator_call_notest(bContext * C,wmOperator * op)1069 int WM_operator_call_notest(bContext *C, wmOperator *op)
1070 {
1071   return wm_operator_exec_notest(C, op);
1072 }
1073 
1074 /**
1075  * Execute this operator again, put here so it can share above code
1076  */
WM_operator_repeat(bContext * C,wmOperator * op)1077 int WM_operator_repeat(bContext *C, wmOperator *op)
1078 {
1079   const int op_flag = OP_IS_REPEAT;
1080   op->flag |= op_flag;
1081   const int ret = wm_operator_exec(C, op, true, true);
1082   op->flag &= ~op_flag;
1083   return ret;
1084 }
WM_operator_repeat_last(bContext * C,wmOperator * op)1085 int WM_operator_repeat_last(bContext *C, wmOperator *op)
1086 {
1087   const int op_flag = OP_IS_REPEAT_LAST;
1088   op->flag |= op_flag;
1089   const int ret = wm_operator_exec(C, op, true, true);
1090   op->flag &= ~op_flag;
1091   return ret;
1092 }
1093 /**
1094  * \return true if #WM_operator_repeat can run.
1095  * Simple check for now but may become more involved.
1096  * To be sure the operator can run call `WM_operator_poll(C, op->type)` also, since this call
1097  * checks if #WM_operator_repeat() can run at all, not that it WILL run at any time.
1098  */
WM_operator_repeat_check(const bContext * UNUSED (C),wmOperator * op)1099 bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
1100 {
1101   if (op->type->exec != NULL) {
1102     return true;
1103   }
1104   if (op->opm) {
1105     /* for macros, check all have exec() we can call */
1106     LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) {
1107       wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
1108       if (otm && otm->exec == NULL) {
1109         return false;
1110       }
1111     }
1112     return true;
1113   }
1114 
1115   return false;
1116 }
1117 
WM_operator_is_repeat(const bContext * C,const wmOperator * op)1118 bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
1119 {
1120   /* May be in the operators list or not. */
1121   wmOperator *op_prev;
1122   if (op->prev == NULL && op->next == NULL) {
1123     wmWindowManager *wm = CTX_wm_manager(C);
1124     op_prev = wm->operators.last;
1125   }
1126   else {
1127     op_prev = op->prev;
1128   }
1129   return (op_prev && (op->type == op_prev->type));
1130 }
1131 
wm_operator_create(wmWindowManager * wm,wmOperatorType * ot,PointerRNA * properties,ReportList * reports)1132 static wmOperator *wm_operator_create(wmWindowManager *wm,
1133                                       wmOperatorType *ot,
1134                                       PointerRNA *properties,
1135                                       ReportList *reports)
1136 {
1137   /* XXX operatortype names are static still. for debug */
1138   wmOperator *op = MEM_callocN(sizeof(wmOperator), ot->idname);
1139 
1140   /* XXX adding new operator could be function, only happens here now */
1141   op->type = ot;
1142   BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
1143 
1144   /* Initialize properties, either copy or create. */
1145   op->ptr = MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
1146   if (properties && properties->data) {
1147     op->properties = IDP_CopyProperty(properties->data);
1148   }
1149   else {
1150     IDPropertyTemplate val = {0};
1151     op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1152   }
1153   RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
1154 
1155   /* Initialize error reports. */
1156   if (reports) {
1157     op->reports = reports; /* Must be initialized already. */
1158   }
1159   else {
1160     op->reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
1161     BKE_reports_init(op->reports, RPT_STORE | RPT_FREE);
1162   }
1163 
1164   /* Recursive filling of operator macro list. */
1165   if (ot->macro.first) {
1166     static wmOperator *motherop = NULL;
1167     int root = 0;
1168 
1169     /* Ensure all ops are in execution order in 1 list. */
1170     if (motherop == NULL) {
1171       motherop = op;
1172       root = 1;
1173     }
1174 
1175     /* If properties exist, it will contain everything needed. */
1176     if (properties) {
1177       wmOperatorTypeMacro *otmacro = ot->macro.first;
1178 
1179       RNA_STRUCT_BEGIN (properties, prop) {
1180 
1181         if (otmacro == NULL) {
1182           break;
1183         }
1184 
1185         /* Skip invalid properties. */
1186         if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
1187           wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1188           PointerRNA someptr = RNA_property_pointer_get(properties, prop);
1189           wmOperator *opm = wm_operator_create(wm, otm, &someptr, NULL);
1190 
1191           IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
1192 
1193           BLI_addtail(&motherop->macro, opm);
1194           opm->opm = motherop; /* Pointer to mom, for modal(). */
1195 
1196           otmacro = otmacro->next;
1197         }
1198       }
1199       RNA_STRUCT_END;
1200     }
1201     else {
1202       LISTBASE_FOREACH (wmOperatorTypeMacro *, macro, &op->opm->type->macro) {
1203         wmOperatorType *otm = WM_operatortype_find(macro->idname, 0);
1204         wmOperator *opm = wm_operator_create(wm, otm, macro->ptr, NULL);
1205 
1206         BLI_addtail(&motherop->macro, opm);
1207         opm->opm = motherop; /* Pointer to mom, for modal(). */
1208       }
1209     }
1210 
1211     if (root) {
1212       motherop = NULL;
1213     }
1214   }
1215 
1216   WM_operator_properties_sanitize(op->ptr, 0);
1217 
1218   return op;
1219 }
1220 
wm_region_mouse_co(bContext * C,wmEvent * event)1221 static void wm_region_mouse_co(bContext *C, wmEvent *event)
1222 {
1223   ARegion *region = CTX_wm_region(C);
1224   if (region) {
1225     /* Compatibility convention. */
1226     event->mval[0] = event->x - region->winrct.xmin;
1227     event->mval[1] = event->y - region->winrct.ymin;
1228   }
1229   else {
1230     /* These values are invalid (avoid odd behavior by relying on old mval values). */
1231     event->mval[0] = -1;
1232     event->mval[1] = -1;
1233   }
1234 }
1235 
1236 /**
1237  * Also used for exec when 'event' is NULL.
1238  */
wm_operator_invoke(bContext * C,wmOperatorType * ot,wmEvent * event,PointerRNA * properties,ReportList * reports,const bool poll_only,bool use_last_properties)1239 static int wm_operator_invoke(bContext *C,
1240                               wmOperatorType *ot,
1241                               wmEvent *event,
1242                               PointerRNA *properties,
1243                               ReportList *reports,
1244                               const bool poll_only,
1245                               bool use_last_properties)
1246 {
1247   int retval = OPERATOR_PASS_THROUGH;
1248 
1249   /* This is done because complicated setup is done to call this function
1250    * that is better not duplicated. */
1251   if (poll_only) {
1252     return WM_operator_poll(C, ot);
1253   }
1254 
1255   if (WM_operator_poll(C, ot)) {
1256     wmWindowManager *wm = CTX_wm_manager(C);
1257 
1258     /* If reports == NULL, they'll be initialized. */
1259     wmOperator *op = wm_operator_create(wm, ot, properties, reports);
1260 
1261     const bool is_nested_call = (wm->op_undo_depth != 0);
1262 
1263     if (event != NULL) {
1264       op->flag |= OP_IS_INVOKE;
1265     }
1266 
1267     /* /initialize setting from previous run. */
1268     if (!is_nested_call && use_last_properties) { /* Not called by py script. */
1269       WM_operator_last_properties_init(op);
1270     }
1271 
1272     if ((event == NULL) || (event->type != MOUSEMOVE)) {
1273       CLOG_INFO(WM_LOG_HANDLERS,
1274                 2,
1275                 "handle evt %d win %p op %s",
1276                 event ? event->type : 0,
1277                 CTX_wm_screen(C)->active_region,
1278                 ot->idname);
1279     }
1280 
1281     if (op->type->invoke && event) {
1282       wm_region_mouse_co(C, event);
1283 
1284       if (op->type->flag & OPTYPE_UNDO) {
1285         wm->op_undo_depth++;
1286       }
1287 
1288       retval = op->type->invoke(C, op, event);
1289       OPERATOR_RETVAL_CHECK(retval);
1290 
1291       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1292         wm->op_undo_depth--;
1293       }
1294     }
1295     else if (op->type->exec) {
1296       if (op->type->flag & OPTYPE_UNDO) {
1297         wm->op_undo_depth++;
1298       }
1299 
1300       retval = op->type->exec(C, op);
1301       OPERATOR_RETVAL_CHECK(retval);
1302 
1303       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1304         wm->op_undo_depth--;
1305       }
1306     }
1307     else {
1308       /* Debug, important to leave a while, should never happen. */
1309       CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
1310     }
1311 
1312     /* Note, if the report is given as an argument then assume the caller will deal with displaying
1313      * them currently Python only uses this. */
1314     if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
1315       /* Only show the report if the report list was not given in the function. */
1316       wm_operator_reports(C, op, retval, (reports != NULL));
1317     }
1318 
1319     if (retval & OPERATOR_HANDLED) {
1320       /* Do nothing, wm_operator_exec() has been called somewhere. */
1321     }
1322     else if (retval & OPERATOR_FINISHED) {
1323       const bool store = !is_nested_call && use_last_properties;
1324       wm_operator_finished(C, op, false, store);
1325     }
1326     else if (retval & OPERATOR_RUNNING_MODAL) {
1327       /* Take ownership of reports (in case python provided own). */
1328       op->reports->flag |= RPT_FREE;
1329 
1330       /* Grab cursor during blocking modal ops (X11)
1331        * Also check for macro.
1332        */
1333       if (ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
1334         int bounds[4] = {-1, -1, -1, -1};
1335         int wrap = WM_CURSOR_WRAP_NONE;
1336 
1337         if (event && (U.uiflag & USER_CONTINUOUS_MOUSE)) {
1338           const wmOperator *op_test = op->opm ? op->opm : op;
1339           const wmOperatorType *ot_test = op_test->type;
1340           if ((ot_test->flag & OPTYPE_GRAB_CURSOR_XY) ||
1341               (op_test->flag & OP_IS_MODAL_GRAB_CURSOR)) {
1342             wrap = WM_CURSOR_WRAP_XY;
1343           }
1344           else if (ot_test->flag & OPTYPE_GRAB_CURSOR_X) {
1345             wrap = WM_CURSOR_WRAP_X;
1346           }
1347           else if (ot_test->flag & OPTYPE_GRAB_CURSOR_Y) {
1348             wrap = WM_CURSOR_WRAP_Y;
1349           }
1350         }
1351 
1352         if (wrap) {
1353           const rcti *winrect = NULL;
1354           ARegion *region = CTX_wm_region(C);
1355           ScrArea *area = CTX_wm_area(C);
1356 
1357           /* Wrap only in X for header. */
1358           if (region && RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
1359             wrap = WM_CURSOR_WRAP_X;
1360           }
1361 
1362           if (region && region->regiontype == RGN_TYPE_WINDOW &&
1363               BLI_rcti_isect_pt_v(&region->winrct, &event->x)) {
1364             winrect = &region->winrct;
1365           }
1366           else if (area && BLI_rcti_isect_pt_v(&area->totrct, &event->x)) {
1367             winrect = &area->totrct;
1368           }
1369 
1370           if (winrect) {
1371             bounds[0] = winrect->xmin;
1372             bounds[1] = winrect->ymax;
1373             bounds[2] = winrect->xmax;
1374             bounds[3] = winrect->ymin;
1375           }
1376         }
1377 
1378         WM_cursor_grab_enable(CTX_wm_window(C), wrap, false, bounds);
1379       }
1380 
1381       /* Cancel UI handlers, typically tooltips that can hang around
1382        * while dragging the view or worse, that stay there permanently
1383        * after the modal operator has swallowed all events and passed
1384        * none to the UI handler. */
1385       wm_event_handler_ui_cancel(C);
1386     }
1387     else {
1388       WM_operator_free(op);
1389     }
1390   }
1391 
1392   return retval;
1393 }
1394 
1395 /**
1396  * #WM_operator_name_call is the main accessor function
1397  * This is for python to access since its done the operator lookup
1398  * invokes operator in context.
1399  */
wm_operator_call_internal(bContext * C,wmOperatorType * ot,PointerRNA * properties,ReportList * reports,const short context,const bool poll_only,wmEvent * event)1400 static int wm_operator_call_internal(bContext *C,
1401                                      wmOperatorType *ot,
1402                                      PointerRNA *properties,
1403                                      ReportList *reports,
1404                                      const short context,
1405                                      const bool poll_only,
1406                                      wmEvent *event)
1407 {
1408   int retval;
1409 
1410   CTX_wm_operator_poll_msg_set(C, NULL);
1411 
1412   /* Dummy test. */
1413   if (ot) {
1414     wmWindow *window = CTX_wm_window(C);
1415 
1416     if (event == NULL) {
1417       switch (context) {
1418         case WM_OP_INVOKE_DEFAULT:
1419         case WM_OP_INVOKE_REGION_WIN:
1420         case WM_OP_INVOKE_REGION_PREVIEW:
1421         case WM_OP_INVOKE_REGION_CHANNELS:
1422         case WM_OP_INVOKE_AREA:
1423         case WM_OP_INVOKE_SCREEN:
1424           /* Window is needed for invoke and cancel operators. */
1425           if (window == NULL) {
1426             if (poll_only) {
1427               CTX_wm_operator_poll_msg_set(C, "Missing 'window' in context");
1428             }
1429             return 0;
1430           }
1431           else {
1432             event = window->eventstate;
1433           }
1434           break;
1435         default:
1436           event = NULL;
1437           break;
1438       }
1439     }
1440     else {
1441       switch (context) {
1442         case WM_OP_EXEC_DEFAULT:
1443         case WM_OP_EXEC_REGION_WIN:
1444         case WM_OP_EXEC_REGION_PREVIEW:
1445         case WM_OP_EXEC_REGION_CHANNELS:
1446         case WM_OP_EXEC_AREA:
1447         case WM_OP_EXEC_SCREEN:
1448           event = NULL;
1449         default:
1450           break;
1451       }
1452     }
1453 
1454     switch (context) {
1455       case WM_OP_EXEC_REGION_WIN:
1456       case WM_OP_INVOKE_REGION_WIN:
1457       case WM_OP_EXEC_REGION_CHANNELS:
1458       case WM_OP_INVOKE_REGION_CHANNELS:
1459       case WM_OP_EXEC_REGION_PREVIEW:
1460       case WM_OP_INVOKE_REGION_PREVIEW: {
1461         /* Forces operator to go to the region window/channels/preview, for header menus,
1462          * but we stay in the same region if we are already in one.
1463          */
1464         ARegion *region = CTX_wm_region(C);
1465         ScrArea *area = CTX_wm_area(C);
1466         int type = RGN_TYPE_WINDOW;
1467 
1468         switch (context) {
1469           case WM_OP_EXEC_REGION_CHANNELS:
1470           case WM_OP_INVOKE_REGION_CHANNELS:
1471             type = RGN_TYPE_CHANNELS;
1472             break;
1473 
1474           case WM_OP_EXEC_REGION_PREVIEW:
1475           case WM_OP_INVOKE_REGION_PREVIEW:
1476             type = RGN_TYPE_PREVIEW;
1477             break;
1478 
1479           case WM_OP_EXEC_REGION_WIN:
1480           case WM_OP_INVOKE_REGION_WIN:
1481           default:
1482             type = RGN_TYPE_WINDOW;
1483             break;
1484         }
1485 
1486         if (!(region && region->regiontype == type) && area) {
1487           ARegion *ar1;
1488           if (type == RGN_TYPE_WINDOW) {
1489             ar1 = BKE_area_find_region_active_win(area);
1490           }
1491           else {
1492             ar1 = BKE_area_find_region_type(area, type);
1493           }
1494 
1495           if (ar1) {
1496             CTX_wm_region_set(C, ar1);
1497           }
1498         }
1499 
1500         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1501 
1502         /* Set region back. */
1503         CTX_wm_region_set(C, region);
1504 
1505         return retval;
1506       }
1507       case WM_OP_EXEC_AREA:
1508       case WM_OP_INVOKE_AREA: {
1509         /* Remove region from context. */
1510         ARegion *region = CTX_wm_region(C);
1511 
1512         CTX_wm_region_set(C, NULL);
1513         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1514         CTX_wm_region_set(C, region);
1515 
1516         return retval;
1517       }
1518       case WM_OP_EXEC_SCREEN:
1519       case WM_OP_INVOKE_SCREEN: {
1520         /* Remove region + area from context. */
1521         ARegion *region = CTX_wm_region(C);
1522         ScrArea *area = CTX_wm_area(C);
1523 
1524         CTX_wm_region_set(C, NULL);
1525         CTX_wm_area_set(C, NULL);
1526         retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1527         CTX_wm_area_set(C, area);
1528         CTX_wm_region_set(C, region);
1529 
1530         return retval;
1531       }
1532       case WM_OP_EXEC_DEFAULT:
1533       case WM_OP_INVOKE_DEFAULT:
1534         return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1535     }
1536   }
1537 
1538   return 0;
1539 }
1540 
1541 /* Invokes operator in context. */
WM_operator_name_call_ptr(bContext * C,wmOperatorType * ot,short context,PointerRNA * properties)1542 int WM_operator_name_call_ptr(bContext *C,
1543                               wmOperatorType *ot,
1544                               short context,
1545                               PointerRNA *properties)
1546 {
1547   BLI_assert(ot == WM_operatortype_find(ot->idname, true));
1548   return wm_operator_call_internal(C, ot, properties, NULL, context, false, NULL);
1549 }
WM_operator_name_call(bContext * C,const char * opstring,short context,PointerRNA * properties)1550 int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
1551 {
1552   wmOperatorType *ot = WM_operatortype_find(opstring, 0);
1553   if (ot) {
1554     return WM_operator_name_call_ptr(C, ot, context, properties);
1555   }
1556 
1557   return 0;
1558 }
1559 
WM_operator_name_call_with_properties(struct bContext * C,const char * opstring,short context,struct IDProperty * properties)1560 int WM_operator_name_call_with_properties(struct bContext *C,
1561                                           const char *opstring,
1562                                           short context,
1563                                           struct IDProperty *properties)
1564 {
1565   PointerRNA props_ptr;
1566   wmOperatorType *ot = WM_operatortype_find(opstring, false);
1567   RNA_pointer_create(NULL, ot->srna, properties, &props_ptr);
1568   return WM_operator_name_call_ptr(C, ot, context, &props_ptr);
1569 }
1570 
1571 /**
1572  * Call an existent menu. The menu can be created in C or Python.
1573  */
WM_menu_name_call(bContext * C,const char * menu_name,short context)1574 void WM_menu_name_call(bContext *C, const char *menu_name, short context)
1575 {
1576   wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
1577   PointerRNA ptr;
1578   WM_operator_properties_create_ptr(&ptr, ot);
1579   RNA_string_set(&ptr, "name", menu_name);
1580   WM_operator_name_call_ptr(C, ot, context, &ptr);
1581   WM_operator_properties_free(&ptr);
1582 }
1583 
1584 /**
1585  * Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context.
1586  *
1587  * - #wmOperatorType is used instead of operator name since python already has the operator type.
1588  * - `poll()` must be called by python before this runs.
1589  * - reports can be passed to this function (so python can report them as exceptions).
1590  */
WM_operator_call_py(bContext * C,wmOperatorType * ot,short context,PointerRNA * properties,ReportList * reports,const bool is_undo)1591 int WM_operator_call_py(bContext *C,
1592                         wmOperatorType *ot,
1593                         short context,
1594                         PointerRNA *properties,
1595                         ReportList *reports,
1596                         const bool is_undo)
1597 {
1598   int retval = OPERATOR_CANCELLED;
1599 
1600 #if 0
1601   wmOperator *op;
1602   op = wm_operator_create(wm, ot, properties, reports);
1603 
1604   if (op->type->exec) {
1605     if (is_undo && op->type->flag & OPTYPE_UNDO) {
1606       wm->op_undo_depth++;
1607     }
1608 
1609     retval = op->type->exec(C, op);
1610     OPERATOR_RETVAL_CHECK(retval);
1611 
1612     if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1613       wm->op_undo_depth--;
1614     }
1615   }
1616   else {
1617     CLOG_WARN(WM_LOG_OPERATORS,
1618               "\"%s\" operator has no exec function, Python cannot call it",
1619               op->type->name);
1620   }
1621 
1622 #endif
1623 
1624   /* Not especially nice using undo depth here. It's used so Python never
1625    * triggers undo or stores an operator's last used state.
1626    *
1627    * We could have some more obvious way of doing this like passing a flag.
1628    */
1629   wmWindowManager *wm = CTX_wm_manager(C);
1630   if (!is_undo && wm) {
1631     wm->op_undo_depth++;
1632   }
1633 
1634   retval = wm_operator_call_internal(C, ot, properties, reports, context, false, NULL);
1635 
1636   if (!is_undo && wm && (wm == CTX_wm_manager(C))) {
1637     wm->op_undo_depth--;
1638   }
1639 
1640   return retval;
1641 }
1642 
1643 /** \} */
1644 
1645 /* -------------------------------------------------------------------- */
1646 /** \name Handler Types
1647  *
1648  * General API for different handler types.
1649  * \{ */
1650 
1651 /* Future extra customadata free? */
wm_event_free_handler(wmEventHandler * handler)1652 void wm_event_free_handler(wmEventHandler *handler)
1653 {
1654   MEM_freeN(handler);
1655 }
1656 
1657 /* Only set context when area/region is part of screen. */
wm_handler_op_context(bContext * C,wmEventHandler_Op * handler,const wmEvent * event)1658 static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
1659 {
1660   wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C);
1661   /* It's probably fine to always use WM_window_get_active_screen() to get the screen. But this
1662    * code has been getting it through context since forever, so play safe and stick to that when
1663    * possible. */
1664   bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C);
1665 
1666   if (screen == NULL || handler->op == NULL) {
1667     return;
1668   }
1669 
1670   if (handler->context.area == NULL) {
1671     CTX_wm_area_set(C, NULL);
1672   }
1673   else {
1674     ScrArea *area = NULL;
1675 
1676     ED_screen_areas_iter (win, screen, area_iter) {
1677       if (area_iter == handler->context.area) {
1678         area = area_iter;
1679         break;
1680       }
1681     }
1682 
1683     if (area == NULL) {
1684       /* When changing screen layouts with running modal handlers (like render display), this
1685        * is not an error to print. */
1686       if (handler->op == NULL) {
1687         CLOG_ERROR(WM_LOG_HANDLERS,
1688                    "internal error: handler (%s) has invalid area",
1689                    handler->op->type->idname);
1690       }
1691     }
1692     else {
1693       ARegion *region;
1694       wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL;
1695       CTX_wm_area_set(C, area);
1696 
1697       if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
1698         region = BKE_area_find_region_xy(area, handler->context.region_type, event->x, event->y);
1699         if (region) {
1700           handler->context.region = region;
1701         }
1702       }
1703       else {
1704         region = NULL;
1705       }
1706 
1707       if (region == NULL) {
1708         for (region = area->regionbase.first; region; region = region->next) {
1709           if (region == handler->context.region) {
1710             break;
1711           }
1712         }
1713       }
1714 
1715       /* XXX no warning print here, after full-area and back regions are remade. */
1716       if (region) {
1717         CTX_wm_region_set(C, region);
1718       }
1719     }
1720   }
1721 }
1722 
1723 /* Called on exit or remove area, only here call cancel callback. */
WM_event_remove_handlers(bContext * C,ListBase * handlers)1724 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
1725 {
1726   wmWindowManager *wm = CTX_wm_manager(C);
1727 
1728   /* C is zero on freeing database, modal handlers then already were freed */
1729   wmEventHandler *handler_base;
1730   while ((handler_base = BLI_pophead(handlers))) {
1731     BLI_assert(handler_base->type != 0);
1732     if (handler_base->type == WM_HANDLER_TYPE_OP) {
1733       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1734       if (handler->op) {
1735         wmWindow *win = CTX_wm_window(C);
1736         if (handler->op->type->cancel) {
1737           ScrArea *area = CTX_wm_area(C);
1738           ARegion *region = CTX_wm_region(C);
1739 
1740           wm_handler_op_context(C, handler, win->eventstate);
1741 
1742           if (handler->op->type->flag & OPTYPE_UNDO) {
1743             wm->op_undo_depth++;
1744           }
1745 
1746           handler->op->type->cancel(C, handler->op);
1747 
1748           if (handler->op->type->flag & OPTYPE_UNDO) {
1749             wm->op_undo_depth--;
1750           }
1751 
1752           CTX_wm_area_set(C, area);
1753           CTX_wm_region_set(C, region);
1754         }
1755 
1756         WM_cursor_grab_disable(win, NULL);
1757         WM_operator_free(handler->op);
1758       }
1759     }
1760     else if (handler_base->type == WM_HANDLER_TYPE_UI) {
1761       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
1762 
1763       if (handler->remove_fn) {
1764         ScrArea *area = CTX_wm_area(C);
1765         ARegion *region = CTX_wm_region(C);
1766         ARegion *menu = CTX_wm_menu(C);
1767 
1768         if (handler->context.area) {
1769           CTX_wm_area_set(C, handler->context.area);
1770         }
1771         if (handler->context.region) {
1772           CTX_wm_region_set(C, handler->context.region);
1773         }
1774         if (handler->context.menu) {
1775           CTX_wm_menu_set(C, handler->context.menu);
1776         }
1777 
1778         handler->remove_fn(C, handler->user_data);
1779 
1780         CTX_wm_area_set(C, area);
1781         CTX_wm_region_set(C, region);
1782         CTX_wm_menu_set(C, menu);
1783       }
1784     }
1785 
1786     wm_event_free_handler(handler_base);
1787   }
1788 }
1789 
wm_eventmatch(const wmEvent * winevent,const wmKeyMapItem * kmi)1790 static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
1791 {
1792   if (kmi->flag & KMI_INACTIVE) {
1793     return false;
1794   }
1795 
1796   if (winevent->is_repeat) {
1797     if (kmi->flag & KMI_REPEAT_IGNORE) {
1798       return false;
1799     }
1800   }
1801 
1802   const int kmitype = WM_userdef_event_map(kmi->type);
1803 
1804   /* The matching rules. */
1805   if (kmitype == KM_TEXTINPUT) {
1806     if (winevent->val == KM_PRESS) { /* Prevent double clicks. */
1807       /* NOT using ISTEXTINPUT anymore because (at least on Windows) some key codes above 255
1808        * could have printable ascii keys - BUG T30479. */
1809       if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) {
1810         return true;
1811       }
1812     }
1813   }
1814 
1815   if (kmitype != KM_ANY) {
1816     if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
1817       const wmTabletData *wmtab = &winevent->tablet;
1818 
1819       if (winevent->type != LEFTMOUSE) {
1820         /* Tablet events can occur on hover + keypress. */
1821         return false;
1822       }
1823       if ((kmitype == TABLET_STYLUS) && (wmtab->active != EVT_TABLET_STYLUS)) {
1824         return false;
1825       }
1826       if ((kmitype == TABLET_ERASER) && (wmtab->active != EVT_TABLET_ERASER)) {
1827         return false;
1828       }
1829     }
1830     else {
1831       if (winevent->type != kmitype) {
1832         return false;
1833       }
1834     }
1835   }
1836 
1837   if (kmi->val != KM_ANY) {
1838     if (winevent->val != kmi->val) {
1839       return false;
1840     }
1841   }
1842 
1843   /* Modifiers also check bits, so it allows modifier order.
1844    * Account for rare case of when these keys are used as the 'type' not as modifiers. */
1845   if (kmi->shift != KM_ANY) {
1846     if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) &&
1847         !ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
1848       return false;
1849     }
1850   }
1851   if (kmi->ctrl != KM_ANY) {
1852     if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) &&
1853         !ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
1854       return false;
1855     }
1856   }
1857   if (kmi->alt != KM_ANY) {
1858     if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) &&
1859         !ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) {
1860       return false;
1861     }
1862   }
1863   if (kmi->oskey != KM_ANY) {
1864     if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) &&
1865         (winevent->type != EVT_OSKEY)) {
1866       return false;
1867     }
1868   }
1869 
1870   /* Only keymap entry with keymodifier is checked,
1871    * means all keys without modifier get handled too. */
1872   /* That is currently needed to make overlapping events work (when you press A - G fast or so). */
1873   if (kmi->keymodifier) {
1874     if (winevent->keymodifier != kmi->keymodifier) {
1875       return false;
1876     }
1877   }
1878 
1879   return true;
1880 }
1881 
wm_eventmatch_modal_keymap_items(const wmKeyMap * keymap,wmOperator * op,const wmEvent * event)1882 static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap,
1883                                                       wmOperator *op,
1884                                                       const wmEvent *event)
1885 {
1886   LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
1887     /* Should already be handled by #wm_user_modal_keymap_set_items. */
1888     BLI_assert(kmi->propvalue_str[0] == '\0');
1889     if (wm_eventmatch(event, kmi)) {
1890       if ((keymap->poll_modal_item == NULL) || (keymap->poll_modal_item(op, kmi->propvalue))) {
1891         return kmi;
1892       }
1893     }
1894   }
1895   return NULL;
1896 }
1897 
1898 /**
1899  * This function prepares events for use with #wmOperatorType.modal by:
1900  *
1901  * - Matching keymap items with the operators modal keymap.
1902  * - Converting double click events into press events,
1903  *   allowing them to be restored when the events aren't handled.
1904  *
1905  *   This is done since we only want to use double click events to match key-map items,
1906  *   allowing modal functions to check for press/release events without having to interpret them.
1907  */
wm_event_modalkeymap_begin(const bContext * C,wmOperator * op,wmEvent * event,bool * dbl_click_disabled)1908 static void wm_event_modalkeymap_begin(const bContext *C,
1909                                        wmOperator *op,
1910                                        wmEvent *event,
1911                                        bool *dbl_click_disabled)
1912 {
1913   BLI_assert(event->type != EVT_MODAL_MAP);
1914 
1915   /* Support for modal keymap in macros. */
1916   if (op->opm) {
1917     op = op->opm;
1918   }
1919 
1920   if (op->type->modalkeymap) {
1921     wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1922     wmKeyMapItem *kmi = NULL;
1923 
1924     const wmEvent *event_match = NULL;
1925     wmEvent event_no_dbl_click;
1926 
1927     if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, event))) {
1928       event_match = event;
1929     }
1930     else if (event->val == KM_DBL_CLICK) {
1931       event_no_dbl_click = *event;
1932       event_no_dbl_click.val = KM_PRESS;
1933       if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, &event_no_dbl_click))) {
1934         event_match = &event_no_dbl_click;
1935       }
1936     }
1937 
1938     if (event_match != NULL) {
1939       event->prevtype = event_match->type;
1940       event->prevval = event_match->val;
1941       event->type = EVT_MODAL_MAP;
1942       event->val = kmi->propvalue;
1943 
1944       /* Avoid double-click events even in the case of 'EVT_MODAL_MAP',
1945        * since it's possible users configure double-click keymap items
1946        * which would break when modal functions expect press/release. */
1947       if (event->prevtype == KM_DBL_CLICK) {
1948         event->prevtype = KM_PRESS;
1949         *dbl_click_disabled = true;
1950       }
1951     }
1952   }
1953 
1954   if (event->type != EVT_MODAL_MAP) {
1955     /* This bypass just disables support for double-click in modal handlers. */
1956     if (event->val == KM_DBL_CLICK) {
1957       event->val = KM_PRESS;
1958       *dbl_click_disabled = true;
1959     }
1960   }
1961 }
1962 
1963 /**
1964  * Restore changes from #wm_event_modalkeymap_begin
1965  *
1966  * \warning bad hacking event system...
1967  * better restore event type for checking of #KM_CLICK for example.
1968  * Modal maps could use different method (ton).
1969  */
wm_event_modalkeymap_end(wmEvent * event,bool dbl_click_disabled)1970 static void wm_event_modalkeymap_end(wmEvent *event, bool dbl_click_disabled)
1971 {
1972   if (event->type == EVT_MODAL_MAP) {
1973     event->type = event->prevtype;
1974     event->prevtype = 0;
1975     event->val = event->prevval;
1976     event->prevval = 0;
1977   }
1978 
1979   if (dbl_click_disabled) {
1980     event->val = KM_DBL_CLICK;
1981   }
1982 }
1983 
1984 /* Warning: this function removes a modal handler, when finished */
wm_handler_operator_call(bContext * C,ListBase * handlers,wmEventHandler * handler_base,wmEvent * event,PointerRNA * properties,const char * kmi_idname)1985 static int wm_handler_operator_call(bContext *C,
1986                                     ListBase *handlers,
1987                                     wmEventHandler *handler_base,
1988                                     wmEvent *event,
1989                                     PointerRNA *properties,
1990                                     const char *kmi_idname)
1991 {
1992   int retval = OPERATOR_PASS_THROUGH;
1993 
1994   /* Derived, modal or blocking operator. */
1995   if ((handler_base->type == WM_HANDLER_TYPE_OP) &&
1996       (((wmEventHandler_Op *)handler_base)->op != NULL)) {
1997     wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
1998     wmOperator *op = handler->op;
1999     wmOperatorType *ot = op->type;
2000 
2001     if (!wm_operator_check_locked_interface(C, ot)) {
2002       /* Interface is locked and operator is not allowed to run,
2003        * nothing to do in this case.
2004        */
2005     }
2006     else if (ot->modal) {
2007       /* We set context to where modal handler came from. */
2008       wmWindowManager *wm = CTX_wm_manager(C);
2009       ScrArea *area = CTX_wm_area(C);
2010       ARegion *region = CTX_wm_region(C);
2011       bool dbl_click_disabled = false;
2012 
2013       wm_handler_op_context(C, handler, event);
2014       wm_region_mouse_co(C, event);
2015       wm_event_modalkeymap_begin(C, op, event, &dbl_click_disabled);
2016 
2017       if (ot->flag & OPTYPE_UNDO) {
2018         wm->op_undo_depth++;
2019       }
2020 
2021       /* Warning, after this call all context data and 'event' may be freed. see check below. */
2022       retval = ot->modal(C, op, event);
2023       OPERATOR_RETVAL_CHECK(retval);
2024 
2025       /* When this is _not_ the case the modal modifier may have loaded
2026        * a new blend file (demo mode does this), so we have to assume
2027        * the event, operator etc have all been freed. - campbell */
2028       if (CTX_wm_manager(C) == wm) {
2029 
2030         wm_event_modalkeymap_end(event, dbl_click_disabled);
2031 
2032         if (ot->flag & OPTYPE_UNDO) {
2033           wm->op_undo_depth--;
2034         }
2035 
2036         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2037           wm_operator_reports(C, op, retval, false);
2038 
2039           if (op->type->modalkeymap) {
2040             wmWindow *win = CTX_wm_window(C);
2041             WM_window_status_area_tag_redraw(win);
2042           }
2043         }
2044         else {
2045           /* Not very common, but modal operators may report before finishing. */
2046           if (!BLI_listbase_is_empty(&op->reports->list)) {
2047             wm_add_reports(op->reports);
2048           }
2049         }
2050 
2051         /* Important to run 'wm_operator_finished' before NULLing the context members. */
2052         if (retval & OPERATOR_FINISHED) {
2053           wm_operator_finished(C, op, false, true);
2054           handler->op = NULL;
2055         }
2056         else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2057           WM_operator_free(op);
2058           handler->op = NULL;
2059         }
2060 
2061         /* Putting back screen context, reval can pass trough after modal failures! */
2062         if ((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
2063           CTX_wm_area_set(C, area);
2064           CTX_wm_region_set(C, region);
2065         }
2066         else {
2067           /* This special cases is for areas and regions that get removed. */
2068           CTX_wm_area_set(C, NULL);
2069           CTX_wm_region_set(C, NULL);
2070         }
2071 
2072         /* /update gizmos during modal handlers. */
2073         wm_gizmomaps_handled_modal_update(C, event, handler);
2074 
2075         /* Remove modal handler, operator itself should have been canceled and freed. */
2076         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2077           WM_cursor_grab_disable(CTX_wm_window(C), NULL);
2078 
2079           BLI_remlink(handlers, handler);
2080           wm_event_free_handler(&handler->head);
2081 
2082           /* prevent silly errors from operator users */
2083           // retval &= ~OPERATOR_PASS_THROUGH;
2084         }
2085       }
2086     }
2087     else {
2088       CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
2089     }
2090   }
2091   else {
2092     wmOperatorType *ot = WM_operatortype_find(kmi_idname, 0);
2093 
2094     if (ot && wm_operator_check_locked_interface(C, ot)) {
2095       bool use_last_properties = true;
2096       PointerRNA tool_properties = {0};
2097 
2098       bToolRef *keymap_tool = NULL;
2099       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2100         keymap_tool = ((wmEventHandler_Keymap *)handler_base)->keymap_tool;
2101       }
2102       else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2103         wmGizmoMap *gizmo_map = ((wmEventHandler_Gizmo *)handler_base)->gizmo_map;
2104         wmGizmo *gz = wm_gizmomap_highlight_get(gizmo_map);
2105         if (gz && (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT)) {
2106           keymap_tool = WM_toolsystem_ref_from_context(C);
2107         }
2108       }
2109 
2110       const bool is_tool = (keymap_tool != NULL);
2111       const bool use_tool_properties = is_tool;
2112 
2113       if (use_tool_properties) {
2114         WM_toolsystem_ref_properties_init_for_keymap(
2115             keymap_tool, &tool_properties, properties, ot);
2116         properties = &tool_properties;
2117         use_last_properties = false;
2118       }
2119 
2120       retval = wm_operator_invoke(C, ot, event, properties, NULL, false, use_last_properties);
2121 
2122       if (use_tool_properties) {
2123         WM_operator_properties_free(&tool_properties);
2124       }
2125 
2126       /* Link gizmo if 'WM_GIZMOGROUPTYPE_TOOL_INIT' is set. */
2127       if (retval & OPERATOR_FINISHED) {
2128         if (is_tool) {
2129           bToolRef_Runtime *tref_rt = keymap_tool->runtime;
2130           if (tref_rt->gizmo_group[0]) {
2131             const char *idname = tref_rt->gizmo_group;
2132             wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
2133             if (gzgt != NULL) {
2134               if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) != 0) {
2135                 ARegion *region = CTX_wm_region(C);
2136                 if (region != NULL) {
2137                   wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
2138                   WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
2139                   wmGizmoGroup *gzgroup = WM_gizmomaptype_group_init_runtime_with_region(
2140                       gzmap_type, gzgt, region);
2141                   /* We can't rely on drawing to initialize gizmo's since disabling
2142                    * overlays/gizmos will prevent pre-drawing setup calls. (see T60905) */
2143                   WM_gizmogroup_ensure_init(C, gzgroup);
2144                 }
2145               }
2146             }
2147           }
2148         }
2149       }
2150       /* Done linking gizmo. */
2151     }
2152   }
2153   /* Finished and pass through flag as handled. */
2154 
2155   /* Finished and pass through flag as handled. */
2156   if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH)) {
2157     return WM_HANDLER_HANDLED;
2158   }
2159 
2160   /* Modal unhandled, break. */
2161   if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL)) {
2162     return (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2163   }
2164 
2165   if (retval & OPERATOR_PASS_THROUGH) {
2166     return WM_HANDLER_CONTINUE;
2167   }
2168 
2169   return WM_HANDLER_BREAK;
2170 }
2171 
2172 /* Fileselect handlers are only in the window queue,
2173  * so it's safe to switch screens or area types. */
wm_handler_fileselect_do(bContext * C,ListBase * handlers,wmEventHandler_Op * handler,int val)2174 static int wm_handler_fileselect_do(bContext *C,
2175                                     ListBase *handlers,
2176                                     wmEventHandler_Op *handler,
2177                                     int val)
2178 {
2179   wmWindowManager *wm = CTX_wm_manager(C);
2180   int action = WM_HANDLER_CONTINUE;
2181 
2182   switch (val) {
2183     case EVT_FILESELECT_FULL_OPEN: {
2184       wmWindow *win = CTX_wm_window(C);
2185       ScrArea *area;
2186 
2187       if ((area = ED_screen_temp_space_open(C,
2188                                             IFACE_("Blender File View"),
2189                                             WM_window_pixels_x(win) / 2,
2190                                             WM_window_pixels_y(win) / 2,
2191                                             U.file_space_data.temp_win_sizex * UI_DPI_FAC,
2192                                             U.file_space_data.temp_win_sizey * UI_DPI_FAC,
2193                                             SPACE_FILE,
2194                                             U.filebrowser_display_type,
2195                                             true))) {
2196         ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
2197 
2198         BLI_assert(area->spacetype == SPACE_FILE);
2199 
2200         region_header->flag |= RGN_FLAG_HIDDEN;
2201         /* Header on bottom, AZone triangle to toggle header looks misplaced at the top. */
2202         region_header->alignment = RGN_ALIGN_BOTTOM;
2203 
2204         /* Settings for filebrowser, #sfile is not operator owner but sends events. */
2205         SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
2206         sfile->op = handler->op;
2207 
2208         ED_fileselect_set_params_from_userdef(sfile);
2209       }
2210       else {
2211         BKE_report(&wm->reports, RPT_ERROR, "Failed to open window!");
2212         return OPERATOR_CANCELLED;
2213       }
2214 
2215       action = WM_HANDLER_BREAK;
2216       break;
2217     }
2218 
2219     case EVT_FILESELECT_EXEC:
2220     case EVT_FILESELECT_CANCEL:
2221     case EVT_FILESELECT_EXTERNAL_CANCEL: {
2222       wmWindow *ctx_win = CTX_wm_window(C);
2223 
2224       /* Remove link now, for load file case before removing. */
2225       BLI_remlink(handlers, handler);
2226 
2227       if (val == EVT_FILESELECT_EXTERNAL_CANCEL) {
2228         /* The window might have been freed already. */
2229         if (BLI_findindex(&wm->windows, handler->context.win) == -1) {
2230           handler->context.win = NULL;
2231         }
2232       }
2233       else {
2234         wmWindow *temp_win;
2235         ScrArea *ctx_area = CTX_wm_area(C);
2236 
2237         for (temp_win = wm->windows.first; temp_win; temp_win = temp_win->next) {
2238           bScreen *screen = WM_window_get_active_screen(temp_win);
2239           ScrArea *file_area = screen->areabase.first;
2240 
2241           if (screen->temp && (file_area->spacetype == SPACE_FILE)) {
2242             int win_size[2];
2243             bool is_maximized;
2244             ED_fileselect_window_params_get(temp_win, win_size, &is_maximized);
2245             ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized);
2246 
2247             if (BLI_listbase_is_single(&file_area->spacedata)) {
2248               BLI_assert(ctx_win != temp_win);
2249 
2250               wm_window_close(C, wm, temp_win);
2251 
2252               CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */
2253               /* Some operators expect a drawable context (for EVT_FILESELECT_EXEC). */
2254               wm_window_make_drawable(wm, ctx_win);
2255               /* Ensure correct cursor position, otherwise, popups may close immediately after
2256                * opening (UI_BLOCK_MOVEMOUSE_QUIT). */
2257               wm_get_cursor_position(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y);
2258               wm->winactive = ctx_win; /* Reports use this... */
2259               if (handler->context.win == temp_win) {
2260                 handler->context.win = NULL;
2261               }
2262             }
2263             else if (file_area->full) {
2264               ED_screen_full_prevspace(C, file_area);
2265             }
2266             else {
2267               ED_area_prevspace(C, file_area);
2268             }
2269 
2270             break;
2271           }
2272         }
2273 
2274         if (!temp_win && ctx_area->full) {
2275           ED_fileselect_params_to_userdef(ctx_area->spacedata.first, NULL, false);
2276           ED_screen_full_prevspace(C, ctx_area);
2277         }
2278       }
2279 
2280       wm_handler_op_context(C, handler, ctx_win->eventstate);
2281 
2282       /* Needed for #UI_popup_menu_reports. */
2283 
2284       if (val == EVT_FILESELECT_EXEC) {
2285         int retval;
2286 
2287         if (handler->op->type->flag & OPTYPE_UNDO) {
2288           wm->op_undo_depth++;
2289         }
2290 
2291         retval = handler->op->type->exec(C, handler->op);
2292 
2293         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2294         if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
2295           wm->op_undo_depth--;
2296         }
2297 
2298         /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2299         if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
2300           if (handler->op->type->flag & OPTYPE_UNDO) {
2301             ED_undo_push_op(C, handler->op);
2302           }
2303           else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED) {
2304             ED_undo_grouped_push_op(C, handler->op);
2305           }
2306         }
2307 
2308         if (handler->op->reports->list.first) {
2309 
2310           /* FIXME, temp setting window, this is really bad!
2311            * only have because lib linking errors need to be seen by users :(
2312            * it can be removed without breaking anything but then no linking errors - campbell */
2313           wmWindow *win_prev = CTX_wm_window(C);
2314           ScrArea *area_prev = CTX_wm_area(C);
2315           ARegion *region_prev = CTX_wm_region(C);
2316 
2317           if (win_prev == NULL) {
2318             CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
2319           }
2320 
2321           BKE_report_print_level_set(handler->op->reports, RPT_WARNING);
2322           UI_popup_menu_reports(C, handler->op->reports);
2323 
2324           /* XXX - copied from 'wm_operator_finished()' */
2325           /* add reports to the global list, otherwise they are not seen */
2326           BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
2327 
2328           /* More hacks, since we meddle with reports, banner display doesn't happen automaticM */
2329           WM_report_banner_show();
2330 
2331           CTX_wm_window_set(C, win_prev);
2332           CTX_wm_area_set(C, area_prev);
2333           CTX_wm_region_set(C, region_prev);
2334         }
2335 
2336         /* For WM_operator_pystring only, custom report handling is done above. */
2337         wm_operator_reports(C, handler->op, retval, true);
2338 
2339         if (retval & OPERATOR_FINISHED) {
2340           WM_operator_last_properties_store(handler->op);
2341         }
2342 
2343         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2344           WM_operator_free(handler->op);
2345         }
2346       }
2347       else {
2348         if (handler->op->type->cancel) {
2349           if (handler->op->type->flag & OPTYPE_UNDO) {
2350             wm->op_undo_depth++;
2351           }
2352 
2353           handler->op->type->cancel(C, handler->op);
2354 
2355           if (handler->op->type->flag & OPTYPE_UNDO) {
2356             wm->op_undo_depth--;
2357           }
2358         }
2359 
2360         WM_operator_free(handler->op);
2361       }
2362 
2363       CTX_wm_area_set(C, NULL);
2364 
2365       wm_event_free_handler(&handler->head);
2366 
2367       action = WM_HANDLER_BREAK;
2368       break;
2369     }
2370   }
2371 
2372   return action;
2373 }
2374 
wm_handler_fileselect_call(bContext * C,ListBase * handlers,wmEventHandler_Op * handler,const wmEvent * event)2375 static int wm_handler_fileselect_call(bContext *C,
2376                                       ListBase *handlers,
2377                                       wmEventHandler_Op *handler,
2378                                       const wmEvent *event)
2379 {
2380   int action = WM_HANDLER_CONTINUE;
2381 
2382   if (event->type != EVT_FILESELECT) {
2383     return action;
2384   }
2385   if (handler->op != (wmOperator *)event->customdata) {
2386     return action;
2387   }
2388 
2389   return wm_handler_fileselect_do(C, handlers, handler, event->val);
2390 }
2391 
wm_action_not_handled(int action)2392 static int wm_action_not_handled(int action)
2393 {
2394   return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2395 }
2396 
2397 #define PRINT \
2398   if (do_debug_handler) \
2399   printf
2400 
wm_handlers_do_keymap_with_keymap_handler(bContext * C,wmEvent * event,ListBase * handlers,wmEventHandler_Keymap * handler,wmKeyMap * keymap,const bool do_debug_handler)2401 static int wm_handlers_do_keymap_with_keymap_handler(
2402     /* From 'wm_handlers_do_intern'. */
2403     bContext *C,
2404     wmEvent *event,
2405     ListBase *handlers,
2406     wmEventHandler_Keymap *handler,
2407     /* Additional. */
2408     wmKeyMap *keymap,
2409     const bool do_debug_handler)
2410 {
2411   int action = WM_HANDLER_CONTINUE;
2412 
2413   if (keymap == NULL) {
2414     /* Only callback is allowed to have NULL keymaps. */
2415     BLI_assert(handler->dynamic.keymap_fn);
2416   }
2417   else {
2418     PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2419 
2420     if (WM_keymap_poll(C, keymap)) {
2421 
2422       PRINT("pass\n");
2423 
2424       LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
2425         if (wm_eventmatch(event, kmi)) {
2426           struct wmEventHandler_KeymapPost keymap_post = handler->post;
2427 
2428           PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2429 
2430           action |= wm_handler_operator_call(
2431               C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
2432 
2433           if (action & WM_HANDLER_BREAK) {
2434             /* Not always_pass here, it denotes removed handler_base. */
2435             CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
2436             if (keymap_post.post_fn != NULL) {
2437               keymap_post.post_fn(keymap, kmi, keymap_post.user_data);
2438             }
2439             break;
2440           }
2441           if (action & WM_HANDLER_HANDLED) {
2442             CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
2443           }
2444           else {
2445             CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
2446           }
2447         }
2448       }
2449     }
2450     else {
2451       PRINT("fail\n");
2452     }
2453   }
2454 
2455   return action;
2456 }
2457 
wm_handlers_do_keymap_with_gizmo_handler(bContext * C,wmEvent * event,ListBase * handlers,wmEventHandler_Gizmo * handler,wmGizmoGroup * gzgroup,wmKeyMap * keymap,const bool do_debug_handler,bool * r_keymap_poll)2458 static int wm_handlers_do_keymap_with_gizmo_handler(
2459     /* From 'wm_handlers_do_intern' */
2460     bContext *C,
2461     wmEvent *event,
2462     ListBase *handlers,
2463     wmEventHandler_Gizmo *handler,
2464     /* Additional. */
2465     wmGizmoGroup *gzgroup,
2466     wmKeyMap *keymap,
2467     const bool do_debug_handler,
2468     bool *r_keymap_poll)
2469 {
2470   int action = WM_HANDLER_CONTINUE;
2471   bool keymap_poll = false;
2472   wmKeyMapItem *kmi;
2473 
2474   PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
2475 
2476   if (WM_keymap_poll(C, keymap)) {
2477     keymap_poll = true;
2478     PRINT("pass\n");
2479     for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2480       if (wm_eventmatch(event, kmi)) {
2481         PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
2482 
2483         CTX_wm_gizmo_group_set(C, gzgroup);
2484 
2485         /* handler->op is called later, we want keymap op to be triggered here. */
2486         action |= wm_handler_operator_call(
2487             C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
2488 
2489         CTX_wm_gizmo_group_set(C, NULL);
2490 
2491         if (action & WM_HANDLER_BREAK) {
2492           if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2493             printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2494           }
2495           break;
2496         }
2497         if (action & WM_HANDLER_HANDLED) {
2498           if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2499             printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
2500           }
2501         }
2502         else {
2503           PRINT("%s:       un-handled '%s'\n", __func__, kmi->idname);
2504         }
2505       }
2506     }
2507   }
2508   else {
2509     PRINT("fail\n");
2510   }
2511 
2512   if (r_keymap_poll) {
2513     *r_keymap_poll = keymap_poll;
2514   }
2515 
2516   return action;
2517 }
2518 
wm_handlers_do_gizmo_handler(bContext * C,wmWindowManager * wm,wmEventHandler_Gizmo * handler,wmEvent * event,ListBase * handlers,const bool do_debug_handler)2519 static int wm_handlers_do_gizmo_handler(bContext *C,
2520                                         wmWindowManager *wm,
2521                                         wmEventHandler_Gizmo *handler,
2522                                         wmEvent *event,
2523                                         ListBase *handlers,
2524                                         const bool do_debug_handler)
2525 {
2526   int action = WM_HANDLER_CONTINUE;
2527   ScrArea *area = CTX_wm_area(C);
2528   ARegion *region = CTX_wm_region(C);
2529   wmGizmoMap *gzmap = handler->gizmo_map;
2530   BLI_assert(gzmap != NULL);
2531   wmGizmo *gz = wm_gizmomap_highlight_get(gzmap);
2532 
2533   /* Needed so UI blocks over gizmos don't let events fall through to the gizmos,
2534    * noticeable for the node editor - where dragging on a node should move it, see: T73212.
2535    * note we still allow for starting the gizmo drag outside, then travel 'inside' the node. */
2536   if (region->type->clip_gizmo_events_by_ui) {
2537     if (UI_region_block_find_mouse_over(region, &event->x, true)) {
2538       if (gz != NULL && event->type != EVT_GIZMO_UPDATE) {
2539         WM_tooltip_clear(C, CTX_wm_window(C));
2540         wm_gizmomap_highlight_set(gzmap, C, NULL, 0);
2541       }
2542       return action;
2543     }
2544   }
2545 
2546   if (region->gizmo_map != handler->gizmo_map) {
2547     WM_gizmomap_tag_refresh(handler->gizmo_map);
2548   }
2549 
2550   wm_gizmomap_handler_context_gizmo(C, handler);
2551   wm_region_mouse_co(C, event);
2552 
2553   /* Drag events use the previous click location to highlight the gizmos,
2554    * Get the highlight again in case the user dragged off the gizmo. */
2555   const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG);
2556   const bool is_event_modifier = ISKEYMODIFIER(event->type);
2557 
2558   bool handle_highlight = false;
2559   bool handle_keymap = false;
2560 
2561   /* Handle gizmo highlighting. */
2562   if (!wm_gizmomap_modal_get(gzmap) &&
2563       ((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag)) {
2564     handle_highlight = true;
2565     if (is_event_modifier || is_event_drag) {
2566       handle_keymap = true;
2567     }
2568   }
2569   else {
2570     handle_keymap = true;
2571   }
2572 
2573   if (handle_highlight) {
2574     struct {
2575       wmGizmo *gz;
2576       int part;
2577     } prev = {
2578         .gz = gz,
2579         .part = gz ? gz->highlight_part : 0,
2580     };
2581     int part = -1;
2582     gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
2583 
2584     /* If no gizmos are/were active, don't clear tool-tips. */
2585     if (gz || prev.gz) {
2586       if ((prev.gz != gz) || (prev.part != part)) {
2587         WM_tooltip_clear(C, CTX_wm_window(C));
2588       }
2589     }
2590 
2591     if (wm_gizmomap_highlight_set(gzmap, C, gz, part)) {
2592       if (gz != NULL) {
2593         if ((U.flag & USER_TOOLTIPS) && (gz->flag & WM_GIZMO_NO_TOOLTIP) == 0) {
2594           WM_tooltip_timer_init(C, CTX_wm_window(C), area, region, WM_gizmomap_tooltip_init);
2595         }
2596       }
2597     }
2598   }
2599 
2600   /* Don't use from now on. */
2601   bool is_event_handle_all = gz && (gz->flag & WM_GIZMO_EVENT_HANDLE_ALL);
2602 
2603   if (handle_keymap) {
2604     /* Handle highlight gizmo. */
2605     if ((gz != NULL) && (gz->flag & WM_GIZMO_HIDDEN_KEYMAP) == 0) {
2606       bool keymap_poll = false;
2607       wmGizmoGroup *gzgroup = gz->parent_gzgroup;
2608       wmKeyMap *keymap = WM_keymap_active(wm, gz->keymap ? gz->keymap : gzgroup->type->keymap);
2609       action |= wm_handlers_do_keymap_with_gizmo_handler(
2610           C, event, handlers, handler, gzgroup, keymap, do_debug_handler, &keymap_poll);
2611 
2612 #ifdef USE_GIZMO_MOUSE_PRIORITY_HACK
2613       if (((action & WM_HANDLER_BREAK) == 0) && !is_event_handle_all && keymap_poll) {
2614         if ((event->val == KM_PRESS) && ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
2615 
2616           wmEvent event_test_click = *event;
2617           event_test_click.val = KM_CLICK;
2618 
2619           wmEvent event_test_click_drag = *event;
2620           event_test_click_drag.val = KM_CLICK_DRAG;
2621 
2622           wmEvent event_test_tweak = *event;
2623           event_test_tweak.type = EVT_TWEAK_L + (event->type - LEFTMOUSE);
2624           event_test_tweak.val = KM_ANY;
2625 
2626           LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
2627             if ((kmi->flag & KMI_INACTIVE) == 0) {
2628               if (wm_eventmatch(&event_test_click, kmi) ||
2629                   wm_eventmatch(&event_test_click_drag, kmi) ||
2630                   wm_eventmatch(&event_test_tweak, kmi)) {
2631                 wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
2632                 if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) {
2633                   is_event_handle_all = true;
2634                   break;
2635                 }
2636               }
2637             }
2638           }
2639         }
2640       }
2641 #endif /* USE_GIZMO_MOUSE_PRIORITY_HACK */
2642     }
2643 
2644     /* Don't use from now on. */
2645     gz = NULL;
2646 
2647     /* Fallback to selected gizmo (when un-handled). */
2648     if ((action & WM_HANDLER_BREAK) == 0) {
2649       if (WM_gizmomap_is_any_selected(gzmap)) {
2650         const ListBase *groups = WM_gizmomap_group_list(gzmap);
2651         LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, groups) {
2652           if (wm_gizmogroup_is_any_selected(gzgroup)) {
2653             wmKeyMap *keymap = WM_keymap_active(wm, gzgroup->type->keymap);
2654             action |= wm_handlers_do_keymap_with_gizmo_handler(
2655                 C, event, handlers, handler, gzgroup, keymap, do_debug_handler, NULL);
2656             if (action & WM_HANDLER_BREAK) {
2657               break;
2658             }
2659           }
2660         }
2661       }
2662     }
2663   }
2664 
2665   if (is_event_handle_all) {
2666     if (action == WM_HANDLER_CONTINUE) {
2667       action |= WM_HANDLER_BREAK | WM_HANDLER_MODAL;
2668     }
2669   }
2670 
2671   /* restore the area */
2672   CTX_wm_area_set(C, area);
2673   CTX_wm_region_set(C, region);
2674 
2675   return action;
2676 }
2677 
2678 /** \} */
2679 
2680 /* -------------------------------------------------------------------- */
2681 /** \name Handle Single Event (All Handler Types)
2682  * \{ */
2683 
wm_handlers_do_intern(bContext * C,wmEvent * event,ListBase * handlers)2684 static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
2685 {
2686   const bool do_debug_handler =
2687       (G.debug & G_DEBUG_HANDLERS) &&
2688       /* Comment this out to flood the console! (if you really want to test). */
2689       !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE);
2690 
2691   wmWindowManager *wm = CTX_wm_manager(C);
2692   int action = WM_HANDLER_CONTINUE;
2693   int always_pass;
2694 
2695   if (handlers == NULL) {
2696     return action;
2697   }
2698 
2699   /* Modal handlers can get removed in this loop, we keep the loop this way.
2700    *
2701    * Note: check 'handlers->first' because in rare cases the handlers can be cleared
2702    * by the event that's called, for eg:
2703    *
2704    * Calling a python script which changes the area.type, see T32232. */
2705   for (wmEventHandler *handler_base = handlers->first, *handler_base_next;
2706        handler_base && handlers->first;
2707        handler_base = handler_base_next) {
2708     handler_base_next = handler_base->next;
2709 
2710     /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
2711     if (handler_base->flag & WM_HANDLER_DO_FREE) {
2712       /* Pass. */
2713     }
2714     else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) {
2715       /* In advance to avoid access to freed event on window close. */
2716       always_pass = wm_event_always_pass(event);
2717 
2718       /* Modal+blocking handler_base. */
2719       if (handler_base->flag & WM_HANDLER_BLOCKING) {
2720         action |= WM_HANDLER_BREAK;
2721       }
2722 
2723       /* Handle all types here. */
2724       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2725         wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
2726         wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
2727         action |= wm_handlers_do_keymap_with_keymap_handler(
2728             C, event, handlers, handler, keymap, do_debug_handler);
2729 
2730         /* Clear the tool-tip whenever a key binding is handled, without this tool-tips
2731          * are kept when a modal operators starts (annoying but otherwise harmless). */
2732         if (action & WM_HANDLER_BREAK) {
2733           /* Window may be gone after file read. */
2734           if (CTX_wm_window(C) != NULL) {
2735             WM_tooltip_clear(C, CTX_wm_window(C));
2736           }
2737         }
2738       }
2739       else if (handler_base->type == WM_HANDLER_TYPE_UI) {
2740         wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
2741         BLI_assert(handler->handle_fn != NULL);
2742         if (!wm->is_interface_locked) {
2743           action |= wm_handler_ui_call(C, handler, event, always_pass);
2744         }
2745       }
2746       else if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
2747         wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
2748         if (!wm->is_interface_locked && event->type == EVT_DROP) {
2749           LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
2750             /* Other drop custom types allowed. */
2751             if (event->custom == EVT_DATA_DRAGDROP) {
2752               ListBase *lb = (ListBase *)event->customdata;
2753               LISTBASE_FOREACH (wmDrag *, drag, lb) {
2754                 const char *tooltip = NULL;
2755                 if (drop->poll(C, drag, event, &tooltip)) {
2756                   /* Optionally copy drag information to operator properties. */
2757                   if (drop->copy) {
2758                     drop->copy(drag, drop);
2759                   }
2760 
2761                   /* Pass single matched wmDrag onto the operator. */
2762                   BLI_remlink(lb, drag);
2763                   ListBase single_lb = {drag, drag};
2764                   event->customdata = &single_lb;
2765 
2766                   wm_operator_call_internal(
2767                       C, drop->ot, drop->ptr, NULL, drop->opcontext, false, event);
2768                   action |= WM_HANDLER_BREAK;
2769 
2770                   /* Free the drags. */
2771                   WM_drag_free_list(lb);
2772                   WM_drag_free_list(&single_lb);
2773 
2774                   event->customdata = NULL;
2775                   event->custom = 0;
2776 
2777                   /* XXX fileread case. */
2778                   if (CTX_wm_window(C) == NULL) {
2779                     return action;
2780                   }
2781 
2782                   /* Escape from drag loop, got freed. */
2783                   break;
2784                 }
2785               }
2786             }
2787           }
2788         }
2789       }
2790       else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2791         wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
2792         action |= wm_handlers_do_gizmo_handler(C, wm, handler, event, handlers, do_debug_handler);
2793       }
2794       else if (handler_base->type == WM_HANDLER_TYPE_OP) {
2795         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2796         if (handler->is_fileselect) {
2797           if (!wm->is_interface_locked) {
2798             /* Screen context changes here. */
2799             action |= wm_handler_fileselect_call(C, handlers, handler, event);
2800           }
2801         }
2802         else {
2803           action |= wm_handler_operator_call(C, handlers, handler_base, event, NULL, NULL);
2804         }
2805       }
2806       else {
2807         /* Unreachable (handle all types above). */
2808         BLI_assert(0);
2809       }
2810 
2811       if (action & WM_HANDLER_BREAK) {
2812         if (always_pass) {
2813           action &= ~WM_HANDLER_BREAK;
2814         }
2815         else {
2816           break;
2817         }
2818       }
2819     }
2820 
2821     /* XXX fileread case, if the wm is freed then the handler's
2822      * will have been too so the code below need not run. */
2823     if (CTX_wm_window(C) == NULL) {
2824       return action;
2825     }
2826 
2827     /* XXX code this for all modal ops, and ensure free only happens here. */
2828 
2829     /* Modal UI handler can be tagged to be freed. */
2830     if (BLI_findindex(handlers, handler_base) !=
2831         -1) { /* Could be freed already by regular modal ops. */
2832       if (handler_base->flag & WM_HANDLER_DO_FREE) {
2833         BLI_remlink(handlers, handler_base);
2834         wm_event_free_handler(handler_base);
2835       }
2836     }
2837   }
2838 
2839   if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL)) {
2840     wm_cursor_arrow_move(CTX_wm_window(C), event);
2841   }
2842 
2843   return action;
2844 }
2845 
2846 #undef PRINT
2847 
2848 /* This calls handlers twice - to solve (double-)click events. */
wm_handlers_do(bContext * C,wmEvent * event,ListBase * handlers)2849 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
2850 {
2851   int action = wm_handlers_do_intern(C, event, handlers);
2852 
2853   /* Fileread case. */
2854   if (CTX_wm_window(C) == NULL) {
2855     return action;
2856   }
2857 
2858   if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2859 
2860     /* Test for CLICK_DRAG events. */
2861     if (wm_action_not_handled(action)) {
2862       if (event->check_drag) {
2863         wmWindow *win = CTX_wm_window(C);
2864         if (WM_event_drag_test(event, &win->eventstate->prevclickx)) {
2865           int x = event->x;
2866           int y = event->y;
2867           short val = event->val;
2868           short type = event->type;
2869 
2870           event->x = win->eventstate->prevclickx;
2871           event->y = win->eventstate->prevclicky;
2872           event->val = KM_CLICK_DRAG;
2873           event->type = win->eventstate->type;
2874 
2875           CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
2876 
2877           action |= wm_handlers_do_intern(C, event, handlers);
2878 
2879           event->val = val;
2880           event->type = type;
2881           event->x = x;
2882           event->y = y;
2883 
2884           win->eventstate->check_click = false;
2885           win->eventstate->check_drag = false;
2886         }
2887       }
2888     }
2889     else {
2890       wmWindow *win = CTX_wm_window(C);
2891       if (win) {
2892         win->eventstate->check_drag = false;
2893       }
2894     }
2895   }
2896   else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) {
2897     /* All events that don't set wmEvent.prevtype must be ignored. */
2898 
2899     /* Test for CLICK events. */
2900     if (wm_action_not_handled(action)) {
2901       wmWindow *win = CTX_wm_window(C);
2902 
2903       /* eventstate stores if previous event was a KM_PRESS, in case that
2904        * wasn't handled, the KM_RELEASE will become a KM_CLICK */
2905 
2906       if (win != NULL) {
2907         if (event->val == KM_PRESS) {
2908           win->eventstate->check_click = true;
2909           win->eventstate->check_drag = true;
2910         }
2911         else if (event->val == KM_RELEASE) {
2912           win->eventstate->check_drag = false;
2913         }
2914       }
2915 
2916       if (win && win->eventstate->prevtype == event->type) {
2917 
2918         if ((event->val == KM_RELEASE) && (win->eventstate->prevval == KM_PRESS) &&
2919             (win->eventstate->check_click == true)) {
2920           if (WM_event_drag_test(event, &win->eventstate->prevclickx)) {
2921             win->eventstate->check_click = 0;
2922             win->eventstate->check_drag = 0;
2923           }
2924           else {
2925             /* Position is where the actual click happens, for more
2926              * accurate selecting in case the mouse drifts a little. */
2927             int x = event->x;
2928             int y = event->y;
2929 
2930             event->x = win->eventstate->prevclickx;
2931             event->y = win->eventstate->prevclicky;
2932             event->val = KM_CLICK;
2933 
2934             CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
2935 
2936             action |= wm_handlers_do_intern(C, event, handlers);
2937 
2938             event->val = KM_RELEASE;
2939             event->x = x;
2940             event->y = y;
2941           }
2942         }
2943         else if (event->val == KM_DBL_CLICK) {
2944           /* The underlying event is a press, so try and handle this. */
2945           event->val = KM_PRESS;
2946           action |= wm_handlers_do_intern(C, event, handlers);
2947 
2948           /* revert value if not handled */
2949           if (wm_action_not_handled(action)) {
2950             event->val = KM_DBL_CLICK;
2951           }
2952         }
2953       }
2954     }
2955     else {
2956       wmWindow *win = CTX_wm_window(C);
2957       if (win) {
2958         win->eventstate->check_click = 0;
2959         win->eventstate->check_drag = 0;
2960       }
2961     }
2962   }
2963   else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) {
2964     /* Modifiers which can trigger click event's,
2965      * however we don't want this if the mouse wheel has been used, see T74607. */
2966     if (wm_action_not_handled(action)) {
2967       /* pass */
2968     }
2969     else {
2970       wmWindow *win = CTX_wm_window(C);
2971       if (win) {
2972         if (ISKEYMODIFIER(win->eventstate->type)) {
2973           win->eventstate->check_click = 0;
2974         }
2975       }
2976     }
2977   }
2978 
2979   return action;
2980 }
2981 
2982 /** \} */
2983 
2984 /* -------------------------------------------------------------------- */
2985 /** \name Event Queue Utilities
2986  *
2987  * Utilities used by #wm_event_do_handlers.
2988  * \{ */
2989 
wm_event_inside_rect(const wmEvent * event,const rcti * rect)2990 static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect)
2991 {
2992   if (wm_event_always_pass(event)) {
2993     return true;
2994   }
2995   if (BLI_rcti_isect_pt_v(rect, &event->x)) {
2996     return true;
2997   }
2998   return false;
2999 }
3000 
wm_event_inside_region(const wmEvent * event,const ARegion * region)3001 static bool wm_event_inside_region(const wmEvent *event, const ARegion *region)
3002 {
3003   if (wm_event_always_pass(event)) {
3004     return true;
3005   }
3006   return ED_region_contains_xy(region, &event->x);
3007 }
3008 
area_event_inside(bContext * C,const int xy[2])3009 static ScrArea *area_event_inside(bContext *C, const int xy[2])
3010 {
3011   wmWindow *win = CTX_wm_window(C);
3012   bScreen *screen = CTX_wm_screen(C);
3013 
3014   if (screen) {
3015     ED_screen_areas_iter (win, screen, area) {
3016       if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
3017         return area;
3018       }
3019     }
3020   }
3021   return NULL;
3022 }
3023 
region_event_inside(bContext * C,const int xy[2])3024 static ARegion *region_event_inside(bContext *C, const int xy[2])
3025 {
3026   bScreen *screen = CTX_wm_screen(C);
3027   ScrArea *area = CTX_wm_area(C);
3028 
3029   if (screen && area) {
3030     LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
3031       if (BLI_rcti_isect_pt_v(&region->winrct, xy)) {
3032         return region;
3033       }
3034     }
3035   }
3036   return NULL;
3037 }
3038 
wm_paintcursor_tag(bContext * C,wmPaintCursor * pc,ARegion * region)3039 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *region)
3040 {
3041   if (region) {
3042     for (; pc; pc = pc->next) {
3043       if (pc->poll == NULL || pc->poll(C)) {
3044         wmWindow *win = CTX_wm_window(C);
3045         WM_paint_cursor_tag_redraw(win, region);
3046       }
3047     }
3048   }
3049 }
3050 
3051 /* Called on mousemove, check updates for paintcursors. */
3052 /* Context was set on active area and region. */
wm_paintcursor_test(bContext * C,const wmEvent * event)3053 static void wm_paintcursor_test(bContext *C, const wmEvent *event)
3054 {
3055   wmWindowManager *wm = CTX_wm_manager(C);
3056 
3057   if (wm->paintcursors.first) {
3058     ARegion *region = CTX_wm_region(C);
3059 
3060     if (region) {
3061       wm_paintcursor_tag(C, wm->paintcursors.first, region);
3062     }
3063 
3064     /* If previous position was not in current region, we have to set a temp new context. */
3065     if (region == NULL || !BLI_rcti_isect_pt_v(&region->winrct, &event->prevx)) {
3066       ScrArea *area = CTX_wm_area(C);
3067 
3068       CTX_wm_area_set(C, area_event_inside(C, &event->prevx));
3069       CTX_wm_region_set(C, region_event_inside(C, &event->prevx));
3070 
3071       wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
3072 
3073       CTX_wm_area_set(C, area);
3074       CTX_wm_region_set(C, region);
3075     }
3076   }
3077 }
3078 
wm_event_drag_and_drop_test(wmWindowManager * wm,wmWindow * win,wmEvent * event)3079 static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
3080 {
3081   bScreen *screen = WM_window_get_active_screen(win);
3082 
3083   if (BLI_listbase_is_empty(&wm->drags)) {
3084     return;
3085   }
3086 
3087   if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
3088     screen->do_draw_drag = true;
3089   }
3090   else if (event->type == EVT_ESCKEY) {
3091     WM_drag_free_list(&wm->drags);
3092 
3093     screen->do_draw_drag = true;
3094   }
3095   else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
3096     event->type = EVT_DROP;
3097 
3098     /* Vreate customdata, first free existing. */
3099     if (event->customdata) {
3100       if (event->customdatafree) {
3101         MEM_freeN(event->customdata);
3102       }
3103     }
3104 
3105     event->custom = EVT_DATA_DRAGDROP;
3106     event->customdata = &wm->drags;
3107     event->customdatafree = 1;
3108 
3109     /* Vlear drop icon. */
3110     screen->do_draw_drag = true;
3111 
3112     /* restore cursor (disabled, see wm_dragdrop.c) */
3113     // WM_cursor_modal_restore(win);
3114   }
3115 }
3116 
3117 /* Filter out all events of the pie that spawned the last pie unless it's a release event. */
wm_event_pie_filter(wmWindow * win,const wmEvent * event)3118 static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
3119 {
3120   if (win->lock_pie_event && win->lock_pie_event == event->type) {
3121     if (event->val == KM_RELEASE) {
3122       win->lock_pie_event = EVENT_NONE;
3123       return false;
3124     }
3125     return true;
3126   }
3127   return false;
3128 }
3129 
3130 /**
3131  * Account for the special case when events are being handled and a file is loaded.
3132  * In this case event handling exits early, however when "Load UI" is disabled
3133  * the even will still be in #wmWindow.queue.
3134  *
3135  * Without this it's possible to continuously handle the same event, see: T76484.
3136  */
wm_event_free_and_remove_from_queue_if_valid(wmEvent * event)3137 static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event)
3138 {
3139   LISTBASE_FOREACH (wmWindowManager *, wm, &G_MAIN->wm) {
3140     LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3141       if (BLI_remlink_safe(&win->queue, event)) {
3142         wm_event_free(event);
3143         return;
3144       }
3145     }
3146   }
3147 }
3148 
3149 /** \} */
3150 
3151 /* -------------------------------------------------------------------- */
3152 /** \name Main Event Queue (Every Window)
3153  *
3154  * Handle events for all windows, run from the #WM_main event loop.
3155  * \{ */
3156 
3157 /* Called in main loop. */
3158 /* Goes over entire hierarchy:  events -> window -> screen -> area -> region. */
wm_event_do_handlers(bContext * C)3159 void wm_event_do_handlers(bContext *C)
3160 {
3161   wmWindowManager *wm = CTX_wm_manager(C);
3162   BLI_assert(ED_undo_is_state_valid(C));
3163 
3164   /* Update key configuration before handling events. */
3165   WM_keyconfig_update(wm);
3166   WM_gizmoconfig_update(CTX_data_main(C));
3167 
3168   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3169     bScreen *screen = WM_window_get_active_screen(win);
3170 
3171     /* Some safety checks - these should always be set! */
3172     BLI_assert(WM_window_get_active_scene(win));
3173     BLI_assert(WM_window_get_active_screen(win));
3174     BLI_assert(WM_window_get_active_workspace(win));
3175 
3176     if (screen == NULL) {
3177       wm_event_free_all(win);
3178     }
3179     else {
3180       Scene *scene = WM_window_get_active_scene(win);
3181       ViewLayer *view_layer = WM_window_get_active_view_layer(win);
3182       Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer);
3183       Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL;
3184 
3185       if (scene_eval != NULL) {
3186         const int is_playing_sound = BKE_sound_scene_playing(scene_eval);
3187 
3188         if (scene_eval->id.recalc & ID_RECALC_AUDIO_SEEK) {
3189           /* Ignore seek here, the audio will be updated to the scene frame after jump during next
3190            * dependency graph update. */
3191         }
3192         else if (is_playing_sound != -1) {
3193           bool is_playing_screen;
3194 
3195           is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
3196 
3197           if (((is_playing_sound == 1) && (is_playing_screen == 0)) ||
3198               ((is_playing_sound == 0) && (is_playing_screen == 1))) {
3199             wmWindow *win_ctx = CTX_wm_window(C);
3200             bScreen *screen_stx = CTX_wm_screen(C);
3201             Scene *scene_ctx = CTX_data_scene(C);
3202 
3203             CTX_wm_window_set(C, win);
3204             CTX_wm_screen_set(C, screen);
3205             CTX_data_scene_set(C, scene);
3206 
3207             ED_screen_animation_play(C, -1, 1);
3208 
3209             CTX_data_scene_set(C, scene_ctx);
3210             CTX_wm_screen_set(C, screen_stx);
3211             CTX_wm_window_set(C, win_ctx);
3212           }
3213 
3214           if (is_playing_sound == 0) {
3215             const double time = BKE_sound_sync_scene(scene_eval);
3216             if (isfinite(time)) {
3217               int ncfra = round(time * FPS);
3218               if (ncfra != scene->r.cfra) {
3219                 scene->r.cfra = ncfra;
3220                 ED_update_for_newframe(CTX_data_main(C), depsgraph);
3221                 WM_event_add_notifier(C, NC_WINDOW, NULL);
3222               }
3223             }
3224           }
3225         }
3226       }
3227     }
3228 
3229     wmEvent *event;
3230     while ((event = win->queue.first)) {
3231       int action = WM_HANDLER_CONTINUE;
3232 
3233       /* Active screen might change during handlers, update pointer. */
3234       screen = WM_window_get_active_screen(win);
3235 
3236       if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) &&
3237           !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3238         printf("\n%s: Handling event\n", __func__);
3239         WM_event_print(event);
3240       }
3241 
3242       /* Take care of pie event filter. */
3243       if (wm_event_pie_filter(win, event)) {
3244         if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3245           CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
3246         }
3247         BLI_remlink(&win->queue, event);
3248         wm_event_free(event);
3249         continue;
3250       }
3251 
3252       CTX_wm_window_set(C, win);
3253 
3254       /* Clear tool-tip on mouse move. */
3255       if (screen->tool_tip && screen->tool_tip->exit_on_event) {
3256         if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
3257           if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, &event->x) > U.move_threshold) {
3258             WM_tooltip_clear(C, win);
3259           }
3260         }
3261       }
3262 
3263       /* We let modal handlers get active area/region, also wm_paintcursor_test needs it. */
3264       CTX_wm_area_set(C, area_event_inside(C, &event->x));
3265       CTX_wm_region_set(C, region_event_inside(C, &event->x));
3266 
3267       /* MVC demands to not draw in event handlers...
3268        * but we need to leave it for ogl selecting etc. */
3269       wm_window_make_drawable(wm, win);
3270 
3271       wm_region_mouse_co(C, event);
3272 
3273       /* First we do priority handlers, modal + some limited keymaps. */
3274       action |= wm_handlers_do(C, event, &win->modalhandlers);
3275 
3276       /* Fileread case. */
3277       if (CTX_wm_window(C) == NULL) {
3278         wm_event_free_and_remove_from_queue_if_valid(event);
3279         return;
3280       }
3281 
3282       /* Check for a tooltip. */
3283       if (screen == WM_window_get_active_screen(win)) {
3284         if (screen->tool_tip && screen->tool_tip->timer) {
3285           if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
3286             WM_tooltip_init(C, win);
3287           }
3288         }
3289       }
3290 
3291       /* Check dragging, creates new event or frees, adds draw tag. */
3292       wm_event_drag_and_drop_test(wm, win, event);
3293 
3294       /* Builtin tweak, if action is break it removes tweak. */
3295       wm_tweakevent_test(C, event, action);
3296 
3297       if ((action & WM_HANDLER_BREAK) == 0) {
3298         /* Note: setting subwin active should be done here, after modal handlers have been done */
3299         if (event->type == MOUSEMOVE) {
3300           /* State variables in screen, cursors.
3301            * Also used in wm_draw.c, fails for modal handlers though. */
3302           ED_screen_set_active_region(C, win, &event->x);
3303           /* For regions having custom cursors. */
3304           wm_paintcursor_test(C, event);
3305         }
3306 #ifdef WITH_INPUT_NDOF
3307         else if (event->type == NDOF_MOTION) {
3308           win->addmousemove = true;
3309         }
3310 #endif
3311 
3312         ED_screen_areas_iter (win, screen, area) {
3313           /* After restoring a screen from SCREENMAXIMIZED we have to wait
3314            * with the screen handling till the region coordinates are updated. */
3315           if (screen->skip_handling == true) {
3316             /* Restore for the next iteration of wm_event_do_handlers. */
3317             screen->skip_handling = false;
3318             break;
3319           }
3320 
3321           /* Update azones if needed - done here because it needs to be independent from redraws.
3322            */
3323           if (area->flag & AREA_FLAG_ACTIONZONES_UPDATE) {
3324             ED_area_azones_update(area, &event->x);
3325           }
3326 
3327           if (wm_event_inside_rect(event, &area->totrct)) {
3328             CTX_wm_area_set(C, area);
3329 
3330             if ((action & WM_HANDLER_BREAK) == 0) {
3331               LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
3332                 if (wm_event_inside_region(event, region)) {
3333 
3334                   CTX_wm_region_set(C, region);
3335 
3336                   /* Call even on non mouse events, since the */
3337                   wm_region_mouse_co(C, event);
3338 
3339                   if (!BLI_listbase_is_empty(&wm->drags)) {
3340                     /* Does polls for drop regions and checks #uiButs. */
3341                     /* Need to be here to make sure region context is true. */
3342                     if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
3343                       wm_drags_check_ops(C, event);
3344                     }
3345                   }
3346 
3347                   action |= wm_handlers_do(C, event, &region->handlers);
3348 
3349                   /* Fileread case (python), T29489. */
3350                   if (CTX_wm_window(C) == NULL) {
3351                     wm_event_free_and_remove_from_queue_if_valid(event);
3352                     return;
3353                   }
3354 
3355                   if (action & WM_HANDLER_BREAK) {
3356                     break;
3357                   }
3358                 }
3359               }
3360             }
3361 
3362             CTX_wm_region_set(C, NULL);
3363 
3364             if ((action & WM_HANDLER_BREAK) == 0) {
3365               wm_region_mouse_co(C, event); /* Only invalidates event->mval in this case. */
3366               action |= wm_handlers_do(C, event, &area->handlers);
3367             }
3368             CTX_wm_area_set(C, NULL);
3369 
3370             /* NOTE: do not escape on WM_HANDLER_BREAK,
3371              * mousemove needs handled for previous area. */
3372           }
3373         }
3374 
3375         if ((action & WM_HANDLER_BREAK) == 0) {
3376           /* Also some non-modal handlers need active area/region. */
3377           CTX_wm_area_set(C, area_event_inside(C, &event->x));
3378           CTX_wm_region_set(C, region_event_inside(C, &event->x));
3379 
3380           wm_region_mouse_co(C, event);
3381 
3382           action |= wm_handlers_do(C, event, &win->handlers);
3383 
3384           /* Fileread case. */
3385           if (CTX_wm_window(C) == NULL) {
3386             wm_event_free_and_remove_from_queue_if_valid(event);
3387             return;
3388           }
3389         }
3390       }
3391 
3392       /* If press was handled, we don't want to do click. This way
3393        * press in tool keymap can override click in editor keymap.*/
3394       if (ISMOUSE_BUTTON(event->type) && event->val == KM_PRESS &&
3395           !wm_action_not_handled(action)) {
3396         win->eventstate->check_click = false;
3397       }
3398 
3399       /* Update previous mouse position for following events to use. */
3400       win->eventstate->prevx = event->x;
3401       win->eventstate->prevy = event->y;
3402 
3403       /* Unlink and free here, blender-quit then frees all. */
3404       BLI_remlink(&win->queue, event);
3405       wm_event_free(event);
3406     }
3407 
3408     /* Only add mousemove when queue was read entirely. */
3409     if (win->addmousemove && win->eventstate) {
3410       wmEvent tevent = *(win->eventstate);
3411       // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
3412       tevent.type = MOUSEMOVE;
3413       tevent.prevx = tevent.x;
3414       tevent.prevy = tevent.y;
3415       tevent.is_repeat = false;
3416       wm_event_add(win, &tevent);
3417       win->addmousemove = 0;
3418     }
3419 
3420     CTX_wm_window_set(C, NULL);
3421   }
3422 
3423   /* Update key configuration after handling events. */
3424   WM_keyconfig_update(wm);
3425   WM_gizmoconfig_update(CTX_data_main(C));
3426 }
3427 
3428 /** \} */
3429 
3430 /* -------------------------------------------------------------------- */
3431 /** \name File Selector Handling
3432  * \{ */
3433 
WM_event_fileselect_event(wmWindowManager * wm,void * ophandle,int eventval)3434 void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval)
3435 {
3436   /* Add to all windows! */
3437   LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3438     wmEvent event = *win->eventstate;
3439 
3440     event.type = EVT_FILESELECT;
3441     event.val = eventval;
3442     event.customdata = ophandle; /* Only as void pointer type check. */
3443 
3444     wm_event_add(win, &event);
3445   }
3446 }
3447 
3448 /* Operator is supposed to have a filled "path" property. */
3449 /* Optional property: filetype (XXX enum?) */
3450 
3451 /**
3452  * The idea here is to keep a handler alive on window queue, owning the operator.
3453  * The file window can send event to make it execute, thus ensuring
3454  * executing happens outside of lower level queues, with UI refreshed.
3455  * Should also allow multiwin solutions.
3456  */
WM_event_add_fileselect(bContext * C,wmOperator * op)3457 void WM_event_add_fileselect(bContext *C, wmOperator *op)
3458 {
3459   wmWindowManager *wm = CTX_wm_manager(C);
3460   wmWindow *win = CTX_wm_window(C);
3461   const bool is_temp_screen = WM_window_is_temp_screen(win);
3462 
3463   /* Close any popups, like when opening a file browser from the splash. */
3464   UI_popup_handlers_remove_all(C, &win->modalhandlers);
3465 
3466   if (!is_temp_screen) {
3467     /* Only allow 1 file selector open per window. */
3468     LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &win->modalhandlers) {
3469       if (handler_base->type == WM_HANDLER_TYPE_OP) {
3470         wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3471         if (handler->is_fileselect == false) {
3472           continue;
3473         }
3474         bScreen *screen = CTX_wm_screen(C);
3475         bool cancel_handler = true;
3476 
3477         /* Find the area with the file selector for this handler. */
3478         ED_screen_areas_iter (win, screen, area) {
3479           if (area->spacetype == SPACE_FILE) {
3480             SpaceFile *sfile = area->spacedata.first;
3481 
3482             if (sfile->op == handler->op) {
3483               CTX_wm_area_set(C, area);
3484               wm_handler_fileselect_do(C, &win->modalhandlers, handler, EVT_FILESELECT_CANCEL);
3485               cancel_handler = false;
3486               break;
3487             }
3488           }
3489         }
3490 
3491         /* If not found we stop the handler without changing the screen. */
3492         if (cancel_handler) {
3493           wm_handler_fileselect_do(
3494               C, &win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL);
3495         }
3496       }
3497     }
3498   }
3499 
3500   wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__);
3501   handler->head.type = WM_HANDLER_TYPE_OP;
3502 
3503   handler->is_fileselect = true;
3504   handler->op = op;
3505   handler->context.win = CTX_wm_window(C);
3506   handler->context.area = CTX_wm_area(C);
3507   handler->context.region = CTX_wm_region(C);
3508 
3509   BLI_addhead(&win->modalhandlers, handler);
3510 
3511   /* Check props once before invoking if check is available
3512    * ensures initial properties are valid. */
3513   if (op->type->check) {
3514     op->type->check(C, op); /* Ignore return value. */
3515   }
3516 
3517   WM_event_fileselect_event(wm, op, EVT_FILESELECT_FULL_OPEN);
3518 }
3519 
3520 /** \} */
3521 
3522 /* -------------------------------------------------------------------- */
3523 /** \name Modal Operator Handling
3524  * \{ */
3525 
3526 #if 0
3527 /* lets not expose struct outside wm? */
3528 static void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
3529 {
3530   handler->flag = flag;
3531 }
3532 #endif
3533 
WM_event_add_modal_handler(bContext * C,wmOperator * op)3534 wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op)
3535 {
3536   wmEventHandler_Op *handler = MEM_callocN(sizeof(*handler), __func__);
3537   handler->head.type = WM_HANDLER_TYPE_OP;
3538   wmWindow *win = CTX_wm_window(C);
3539 
3540   /* Operator was part of macro. */
3541   if (op->opm) {
3542     /* Give the mother macro to the handler. */
3543     handler->op = op->opm;
3544     /* Mother macro opm becomes the macro element. */
3545     handler->op->opm = op;
3546   }
3547   else {
3548     handler->op = op;
3549   }
3550 
3551   handler->context.area = CTX_wm_area(C); /* Means frozen screen context for modal handlers! */
3552   handler->context.region = CTX_wm_region(C);
3553   handler->context.region_type = handler->context.region ? handler->context.region->regiontype :
3554                                                            -1;
3555 
3556   BLI_addhead(&win->modalhandlers, handler);
3557 
3558   if (op->type->modalkeymap) {
3559     WM_window_status_area_tag_redraw(win);
3560   }
3561 
3562   return handler;
3563 }
3564 
3565 /**
3566  * Modal handlers store a pointer to an area which might be freed while the handler runs.
3567  * Use this function to NULL all handler pointers to \a old_area.
3568  */
WM_event_modal_handler_area_replace(wmWindow * win,const ScrArea * old_area,ScrArea * new_area)3569 void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
3570 {
3571   LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
3572     if (handler_base->type == WM_HANDLER_TYPE_OP) {
3573       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3574       /* Fileselect handler is quite special...
3575        * it needs to keep old area stored in handler, so don't change it. */
3576       if ((handler->context.area == old_area) && (handler->is_fileselect == false)) {
3577         handler->context.area = new_area;
3578       }
3579     }
3580   }
3581 }
3582 
3583 /**
3584  * Modal handlers store a pointer to a region which might be freed while the handler runs.
3585  * Use this function to NULL all handler pointers to \a old_region.
3586  */
WM_event_modal_handler_region_replace(wmWindow * win,const ARegion * old_region,ARegion * new_region)3587 void WM_event_modal_handler_region_replace(wmWindow *win,
3588                                            const ARegion *old_region,
3589                                            ARegion *new_region)
3590 {
3591   LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
3592     if (handler_base->type == WM_HANDLER_TYPE_OP) {
3593       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3594       /* Fileselect handler is quite special...
3595        * it needs to keep old region stored in handler, so don't change it. */
3596       if ((handler->context.region == old_region) && (handler->is_fileselect == false)) {
3597         handler->context.region = new_region;
3598         handler->context.region_type = new_region ? new_region->regiontype : RGN_TYPE_WINDOW;
3599       }
3600     }
3601   }
3602 }
3603 
WM_event_add_keymap_handler(ListBase * handlers,wmKeyMap * keymap)3604 wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
3605 {
3606   if (!keymap) {
3607     CLOG_WARN(WM_LOG_HANDLERS, "called with NULL keymap");
3608     return NULL;
3609   }
3610 
3611   /* Only allow same keymap once. */
3612   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
3613     if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
3614       wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
3615       if (handler->keymap == keymap) {
3616         return handler;
3617       }
3618     }
3619   }
3620 
3621   wmEventHandler_Keymap *handler = MEM_callocN(sizeof(*handler), __func__);
3622   handler->head.type = WM_HANDLER_TYPE_KEYMAP;
3623   BLI_addtail(handlers, handler);
3624   handler->keymap = keymap;
3625 
3626   return handler;
3627 }
3628 
3629 /**
3630  * Implements fallback tool when enabled by:
3631  * #SCE_WORKSPACE_TOOL_FALLBACK, #WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP.
3632  *
3633  * This runs before #WM_event_get_keymap_from_toolsystem,
3634  * allowing both the fallback-tool and active-tool to be activated
3635  * providing the key-map is configured so the keys don't conflict.
3636  * For example one mouse button can run the active-tool, another button for the fallback-tool.
3637  * See T72567.
3638  *
3639  * Follow #wmEventHandler_KeymapDynamicFn signature.
3640  */
WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager * wm,wmEventHandler_Keymap * handler)3641 wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm,
3642                                                        wmEventHandler_Keymap *handler)
3643 {
3644   ScrArea *area = handler->dynamic.user_data;
3645   handler->keymap_tool = NULL;
3646   bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL;
3647   if (tref_rt && tref_rt->keymap_fallback[0]) {
3648     const char *keymap_id = NULL;
3649 
3650     /* Support for the gizmo owning the tool keymap. */
3651     if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\n') {
3652       wmGizmoMap *gzmap = NULL;
3653       wmGizmoGroup *gzgroup = NULL;
3654       LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
3655         if (region->gizmo_map != NULL) {
3656           gzmap = region->gizmo_map;
3657           gzgroup = WM_gizmomap_group_find(gzmap, tref_rt->gizmo_group);
3658           if (gzgroup != NULL) {
3659             break;
3660           }
3661         }
3662       }
3663       if (gzgroup != NULL) {
3664         if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) {
3665           /* If all are hidden, don't override. */
3666           if (gzgroup->use_fallback_keymap) {
3667             wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap);
3668             if (highlight == NULL) {
3669               keymap_id = tref_rt->keymap_fallback;
3670             }
3671           }
3672         }
3673       }
3674     }
3675 
3676     if (keymap_id && keymap_id[0]) {
3677       wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty(
3678           &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW);
3679       /* We shouldn't use keymaps from unrelated spaces. */
3680       if (km != NULL) {
3681         handler->keymap_tool = area->runtime.tool;
3682         return km;
3683       }
3684       printf(
3685           "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname);
3686     }
3687   }
3688   return NULL;
3689 }
3690 
WM_event_get_keymap_from_toolsystem(wmWindowManager * wm,wmEventHandler_Keymap * handler)3691 wmKeyMap *WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, wmEventHandler_Keymap *handler)
3692 {
3693   ScrArea *area = handler->dynamic.user_data;
3694   handler->keymap_tool = NULL;
3695   bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL;
3696   if (tref_rt && tref_rt->keymap[0]) {
3697     const char *keymap_id = tref_rt->keymap;
3698     {
3699       wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty(
3700           &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW);
3701       /* We shouldn't use keymaps from unrelated spaces. */
3702       if (km != NULL) {
3703         handler->keymap_tool = area->runtime.tool;
3704         return km;
3705       }
3706       printf(
3707           "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname);
3708     }
3709   }
3710   return NULL;
3711 }
3712 
WM_event_add_keymap_handler_dynamic(ListBase * handlers,wmEventHandler_KeymapDynamicFn * keymap_fn,void * user_data)3713 struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic(
3714     ListBase *handlers, wmEventHandler_KeymapDynamicFn *keymap_fn, void *user_data)
3715 {
3716   if (!keymap_fn) {
3717     CLOG_WARN(WM_LOG_HANDLERS, "called with NULL keymap_fn");
3718     return NULL;
3719   }
3720 
3721   /* Only allow same keymap once. */
3722   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
3723     if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
3724       wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
3725       if (handler->dynamic.keymap_fn == keymap_fn) {
3726         /* Maximizing the view needs to update the area. */
3727         handler->dynamic.user_data = user_data;
3728         return handler;
3729       }
3730     }
3731   }
3732 
3733   wmEventHandler_Keymap *handler = MEM_callocN(sizeof(*handler), __func__);
3734   handler->head.type = WM_HANDLER_TYPE_KEYMAP;
3735   BLI_addtail(handlers, handler);
3736   handler->dynamic.keymap_fn = keymap_fn;
3737   handler->dynamic.user_data = user_data;
3738 
3739   return handler;
3740 }
3741 
3742 /* Priorities not implemented yet, for time being just insert in begin of list. */
WM_event_add_keymap_handler_priority(ListBase * handlers,wmKeyMap * keymap,int UNUSED (priority))3743 wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *handlers,
3744                                                             wmKeyMap *keymap,
3745                                                             int UNUSED(priority))
3746 {
3747   WM_event_remove_keymap_handler(handlers, keymap);
3748 
3749   wmEventHandler_Keymap *handler = MEM_callocN(sizeof(*handler), "event keymap handler");
3750   handler->head.type = WM_HANDLER_TYPE_KEYMAP;
3751 
3752   BLI_addhead(handlers, handler);
3753   handler->keymap = keymap;
3754 
3755   return handler;
3756 }
3757 
event_or_prev_in_rect(const wmEvent * event,const rcti * rect)3758 static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect)
3759 {
3760   if (BLI_rcti_isect_pt(rect, event->x, event->y)) {
3761     return true;
3762   }
3763   if (event->type == MOUSEMOVE && BLI_rcti_isect_pt(rect, event->prevx, event->prevy)) {
3764     return true;
3765   }
3766   return false;
3767 }
3768 
handler_region_v2d_mask_test(const ARegion * region,const wmEvent * event)3769 static bool handler_region_v2d_mask_test(const ARegion *region, const wmEvent *event)
3770 {
3771   rcti rect = region->v2d.mask;
3772   BLI_rcti_translate(&rect, region->winrct.xmin, region->winrct.ymin);
3773   return event_or_prev_in_rect(event, &rect);
3774 }
3775 
WM_event_add_keymap_handler_poll(ListBase * handlers,wmKeyMap * keymap,EventHandlerPoll poll)3776 wmEventHandler_Keymap *WM_event_add_keymap_handler_poll(ListBase *handlers,
3777                                                         wmKeyMap *keymap,
3778                                                         EventHandlerPoll poll)
3779 {
3780   wmEventHandler_Keymap *handler = WM_event_add_keymap_handler(handlers, keymap);
3781   if (handler == NULL) {
3782     return NULL;
3783   }
3784 
3785   handler->head.poll = poll;
3786   return handler;
3787 }
3788 
WM_event_add_keymap_handler_v2d_mask(ListBase * handlers,wmKeyMap * keymap)3789 wmEventHandler_Keymap *WM_event_add_keymap_handler_v2d_mask(ListBase *handlers, wmKeyMap *keymap)
3790 {
3791   return WM_event_add_keymap_handler_poll(handlers, keymap, handler_region_v2d_mask_test);
3792 }
3793 
WM_event_remove_keymap_handler(ListBase * handlers,wmKeyMap * keymap)3794 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
3795 {
3796   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
3797     if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
3798       wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
3799       if (handler->keymap == keymap) {
3800         BLI_remlink(handlers, handler);
3801         wm_event_free_handler(&handler->head);
3802         break;
3803       }
3804     }
3805   }
3806 }
3807 
WM_event_set_keymap_handler_post_callback(wmEventHandler_Keymap * handler,void (keymap_tag)(wmKeyMap * keymap,wmKeyMapItem * kmi,void * user_data),void * user_data)3808 void WM_event_set_keymap_handler_post_callback(wmEventHandler_Keymap *handler,
3809                                                void(keymap_tag)(wmKeyMap *keymap,
3810                                                                 wmKeyMapItem *kmi,
3811                                                                 void *user_data),
3812                                                void *user_data)
3813 {
3814   handler->post.post_fn = keymap_tag;
3815   handler->post.user_data = user_data;
3816 }
3817 
WM_event_add_ui_handler(const bContext * C,ListBase * handlers,wmUIHandlerFunc handle_fn,wmUIHandlerRemoveFunc remove_fn,void * user_data,const char flag)3818 wmEventHandler_UI *WM_event_add_ui_handler(const bContext *C,
3819                                            ListBase *handlers,
3820                                            wmUIHandlerFunc handle_fn,
3821                                            wmUIHandlerRemoveFunc remove_fn,
3822                                            void *user_data,
3823                                            const char flag)
3824 {
3825   wmEventHandler_UI *handler = MEM_callocN(sizeof(*handler), __func__);
3826   handler->head.type = WM_HANDLER_TYPE_UI;
3827   handler->handle_fn = handle_fn;
3828   handler->remove_fn = remove_fn;
3829   handler->user_data = user_data;
3830   if (C) {
3831     handler->context.area = CTX_wm_area(C);
3832     handler->context.region = CTX_wm_region(C);
3833     handler->context.menu = CTX_wm_menu(C);
3834   }
3835   else {
3836     handler->context.area = NULL;
3837     handler->context.region = NULL;
3838     handler->context.menu = NULL;
3839   }
3840 
3841   BLI_assert((flag & WM_HANDLER_DO_FREE) == 0);
3842   handler->head.flag = flag;
3843 
3844   BLI_addhead(handlers, handler);
3845 
3846   return handler;
3847 }
3848 
3849 /* Set "postpone" for win->modalhandlers, this is in a running for () loop in wm_handlers_do(). */
WM_event_remove_ui_handler(ListBase * handlers,wmUIHandlerFunc handle_fn,wmUIHandlerRemoveFunc remove_fn,void * user_data,const bool postpone)3850 void WM_event_remove_ui_handler(ListBase *handlers,
3851                                 wmUIHandlerFunc handle_fn,
3852                                 wmUIHandlerRemoveFunc remove_fn,
3853                                 void *user_data,
3854                                 const bool postpone)
3855 {
3856   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
3857     if (handler_base->type == WM_HANDLER_TYPE_UI) {
3858       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
3859       if ((handler->handle_fn == handle_fn) && (handler->remove_fn == remove_fn) &&
3860           (handler->user_data == user_data)) {
3861         /* Handlers will be freed in wm_handlers_do(). */
3862         if (postpone) {
3863           handler->head.flag |= WM_HANDLER_DO_FREE;
3864         }
3865         else {
3866           BLI_remlink(handlers, handler);
3867           wm_event_free_handler(&handler->head);
3868         }
3869         break;
3870       }
3871     }
3872   }
3873 }
3874 
WM_event_free_ui_handler_all(bContext * C,ListBase * handlers,wmUIHandlerFunc handle_fn,wmUIHandlerRemoveFunc remove_fn)3875 void WM_event_free_ui_handler_all(bContext *C,
3876                                   ListBase *handlers,
3877                                   wmUIHandlerFunc handle_fn,
3878                                   wmUIHandlerRemoveFunc remove_fn)
3879 {
3880   LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, handlers) {
3881     if (handler_base->type == WM_HANDLER_TYPE_UI) {
3882       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
3883       if ((handler->handle_fn == handle_fn) && (handler->remove_fn == remove_fn)) {
3884         remove_fn(C, handler->user_data);
3885         BLI_remlink(handlers, handler);
3886         wm_event_free_handler(&handler->head);
3887       }
3888     }
3889   }
3890 }
3891 
WM_event_add_dropbox_handler(ListBase * handlers,ListBase * dropboxes)3892 wmEventHandler_Dropbox *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
3893 {
3894   /* Only allow same dropbox once. */
3895   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
3896     if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
3897       wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
3898       if (handler->dropboxes == dropboxes) {
3899         return handler;
3900       }
3901     }
3902   }
3903 
3904   wmEventHandler_Dropbox *handler = MEM_callocN(sizeof(*handler), __func__);
3905   handler->head.type = WM_HANDLER_TYPE_DROPBOX;
3906 
3907   /* Dropbox stored static, no free or copy. */
3908   handler->dropboxes = dropboxes;
3909   BLI_addhead(handlers, handler);
3910 
3911   return handler;
3912 }
3913 
3914 /* XXX solution works, still better check the real cause (ton) */
WM_event_remove_area_handler(ListBase * handlers,void * area)3915 void WM_event_remove_area_handler(ListBase *handlers, void *area)
3916 {
3917   LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, handlers) {
3918     if (handler_base->type == WM_HANDLER_TYPE_UI) {
3919       wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
3920       if (handler->context.area == area) {
3921         BLI_remlink(handlers, handler);
3922         wm_event_free_handler(handler_base);
3923       }
3924     }
3925   }
3926 }
3927 
3928 #if 0
3929 static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
3930 {
3931   BLI_remlink(handlers, handler);
3932   wm_event_free_handler(handler);
3933 }
3934 #endif
3935 
WM_event_add_mousemove(wmWindow * win)3936 void WM_event_add_mousemove(wmWindow *win)
3937 {
3938   win->addmousemove = 1;
3939 }
3940 
3941 /** \} */
3942 
3943 /* -------------------------------------------------------------------- */
3944 /** \name Ghost Event Conversion
3945  * \{ */
3946 
convert_key(GHOST_TKey key)3947 static int convert_key(GHOST_TKey key)
3948 {
3949   if (key >= GHOST_kKeyA && key <= GHOST_kKeyZ) {
3950     return (EVT_AKEY + ((int)key - GHOST_kKeyA));
3951   }
3952   if (key >= GHOST_kKey0 && key <= GHOST_kKey9) {
3953     return (EVT_ZEROKEY + ((int)key - GHOST_kKey0));
3954   }
3955   if (key >= GHOST_kKeyNumpad0 && key <= GHOST_kKeyNumpad9) {
3956     return (EVT_PAD0 + ((int)key - GHOST_kKeyNumpad0));
3957   }
3958   if (key >= GHOST_kKeyF1 && key <= GHOST_kKeyF24) {
3959     return (EVT_F1KEY + ((int)key - GHOST_kKeyF1));
3960   }
3961 
3962   switch (key) {
3963     case GHOST_kKeyBackSpace:
3964       return EVT_BACKSPACEKEY;
3965     case GHOST_kKeyTab:
3966       return EVT_TABKEY;
3967     case GHOST_kKeyLinefeed:
3968       return EVT_LINEFEEDKEY;
3969     case GHOST_kKeyClear:
3970       return 0;
3971     case GHOST_kKeyEnter:
3972       return EVT_RETKEY;
3973 
3974     case GHOST_kKeyEsc:
3975       return EVT_ESCKEY;
3976     case GHOST_kKeySpace:
3977       return EVT_SPACEKEY;
3978     case GHOST_kKeyQuote:
3979       return EVT_QUOTEKEY;
3980     case GHOST_kKeyComma:
3981       return EVT_COMMAKEY;
3982     case GHOST_kKeyMinus:
3983       return EVT_MINUSKEY;
3984     case GHOST_kKeyPlus:
3985       return EVT_PLUSKEY;
3986     case GHOST_kKeyPeriod:
3987       return EVT_PERIODKEY;
3988     case GHOST_kKeySlash:
3989       return EVT_SLASHKEY;
3990 
3991     case GHOST_kKeySemicolon:
3992       return EVT_SEMICOLONKEY;
3993     case GHOST_kKeyEqual:
3994       return EVT_EQUALKEY;
3995 
3996     case GHOST_kKeyLeftBracket:
3997       return EVT_LEFTBRACKETKEY;
3998     case GHOST_kKeyRightBracket:
3999       return EVT_RIGHTBRACKETKEY;
4000     case GHOST_kKeyBackslash:
4001       return EVT_BACKSLASHKEY;
4002     case GHOST_kKeyAccentGrave:
4003       return EVT_ACCENTGRAVEKEY;
4004 
4005     case GHOST_kKeyLeftShift:
4006       return EVT_LEFTSHIFTKEY;
4007     case GHOST_kKeyRightShift:
4008       return EVT_RIGHTSHIFTKEY;
4009     case GHOST_kKeyLeftControl:
4010       return EVT_LEFTCTRLKEY;
4011     case GHOST_kKeyRightControl:
4012       return EVT_RIGHTCTRLKEY;
4013     case GHOST_kKeyOS:
4014       return EVT_OSKEY;
4015     case GHOST_kKeyLeftAlt:
4016       return EVT_LEFTALTKEY;
4017     case GHOST_kKeyRightAlt:
4018       return EVT_RIGHTALTKEY;
4019     case GHOST_kKeyApp:
4020       return EVT_APPKEY;
4021 
4022     case GHOST_kKeyCapsLock:
4023       return EVT_CAPSLOCKKEY;
4024     case GHOST_kKeyNumLock:
4025       return 0;
4026     case GHOST_kKeyScrollLock:
4027       return 0;
4028 
4029     case GHOST_kKeyLeftArrow:
4030       return EVT_LEFTARROWKEY;
4031     case GHOST_kKeyRightArrow:
4032       return EVT_RIGHTARROWKEY;
4033     case GHOST_kKeyUpArrow:
4034       return EVT_UPARROWKEY;
4035     case GHOST_kKeyDownArrow:
4036       return EVT_DOWNARROWKEY;
4037 
4038     case GHOST_kKeyPrintScreen:
4039       return 0;
4040     case GHOST_kKeyPause:
4041       return EVT_PAUSEKEY;
4042 
4043     case GHOST_kKeyInsert:
4044       return EVT_INSERTKEY;
4045     case GHOST_kKeyDelete:
4046       return EVT_DELKEY;
4047     case GHOST_kKeyHome:
4048       return EVT_HOMEKEY;
4049     case GHOST_kKeyEnd:
4050       return EVT_ENDKEY;
4051     case GHOST_kKeyUpPage:
4052       return EVT_PAGEUPKEY;
4053     case GHOST_kKeyDownPage:
4054       return EVT_PAGEDOWNKEY;
4055 
4056     case GHOST_kKeyNumpadPeriod:
4057       return EVT_PADPERIOD;
4058     case GHOST_kKeyNumpadEnter:
4059       return EVT_PADENTER;
4060     case GHOST_kKeyNumpadPlus:
4061       return EVT_PADPLUSKEY;
4062     case GHOST_kKeyNumpadMinus:
4063       return EVT_PADMINUS;
4064     case GHOST_kKeyNumpadAsterisk:
4065       return EVT_PADASTERKEY;
4066     case GHOST_kKeyNumpadSlash:
4067       return EVT_PADSLASHKEY;
4068 
4069     case GHOST_kKeyGrLess:
4070       return EVT_GRLESSKEY;
4071 
4072     case GHOST_kKeyMediaPlay:
4073       return EVT_MEDIAPLAY;
4074     case GHOST_kKeyMediaStop:
4075       return EVT_MEDIASTOP;
4076     case GHOST_kKeyMediaFirst:
4077       return EVT_MEDIAFIRST;
4078     case GHOST_kKeyMediaLast:
4079       return EVT_MEDIALAST;
4080 
4081     default:
4082       return EVT_UNKNOWNKEY; /* GHOST_kKeyUnknown */
4083   }
4084 }
4085 
wm_eventemulation(wmEvent * event,bool test_only)4086 static void wm_eventemulation(wmEvent *event, bool test_only)
4087 {
4088   /* Store last middle-mouse event value to make emulation work
4089    * when modifier keys are released first.
4090    * This really should be in a data structure somewhere. */
4091   static int emulating_event = EVENT_NONE;
4092 
4093   /* Middle-mouse emulation. */
4094   if (U.flag & USER_TWOBUTTONMOUSE) {
4095 
4096     if (event->type == LEFTMOUSE) {
4097       short *mod = (
4098 #if !defined(WIN32)
4099           (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? &event->oskey :
4100                                                                           &event->alt
4101 #else
4102           /* Disable for WIN32 for now because it accesses the start menu. */
4103           &event->alt
4104 #endif
4105       );
4106 
4107       if (event->val == KM_PRESS) {
4108         if (*mod) {
4109           *mod = 0;
4110           event->type = MIDDLEMOUSE;
4111 
4112           if (!test_only) {
4113             emulating_event = MIDDLEMOUSE;
4114           }
4115         }
4116       }
4117       else if (event->val == KM_RELEASE) {
4118         /* Only send middle-mouse release if emulated. */
4119         if (emulating_event == MIDDLEMOUSE) {
4120           event->type = MIDDLEMOUSE;
4121           *mod = 0;
4122         }
4123 
4124         if (!test_only) {
4125           emulating_event = EVENT_NONE;
4126         }
4127       }
4128     }
4129   }
4130 
4131   /* Numpad emulation. */
4132   if (U.flag & USER_NONUMPAD) {
4133     switch (event->type) {
4134       case EVT_ZEROKEY:
4135         event->type = EVT_PAD0;
4136         break;
4137       case EVT_ONEKEY:
4138         event->type = EVT_PAD1;
4139         break;
4140       case EVT_TWOKEY:
4141         event->type = EVT_PAD2;
4142         break;
4143       case EVT_THREEKEY:
4144         event->type = EVT_PAD3;
4145         break;
4146       case EVT_FOURKEY:
4147         event->type = EVT_PAD4;
4148         break;
4149       case EVT_FIVEKEY:
4150         event->type = EVT_PAD5;
4151         break;
4152       case EVT_SIXKEY:
4153         event->type = EVT_PAD6;
4154         break;
4155       case EVT_SEVENKEY:
4156         event->type = EVT_PAD7;
4157         break;
4158       case EVT_EIGHTKEY:
4159         event->type = EVT_PAD8;
4160         break;
4161       case EVT_NINEKEY:
4162         event->type = EVT_PAD9;
4163         break;
4164       case EVT_MINUSKEY:
4165         event->type = EVT_PADMINUS;
4166         break;
4167       case EVT_EQUALKEY:
4168         event->type = EVT_PADPLUSKEY;
4169         break;
4170       case EVT_BACKSLASHKEY:
4171         event->type = EVT_PADSLASHKEY;
4172         break;
4173     }
4174   }
4175 }
4176 
4177 static const wmTabletData wm_event_tablet_data_default = {
4178     .active = EVT_TABLET_NONE,
4179     .pressure = 1.0f,
4180     .x_tilt = 0.0f,
4181     .y_tilt = 0.0f,
4182     .is_motion_absolute = false,
4183 };
4184 
WM_event_tablet_data_default_set(wmTabletData * tablet_data)4185 void WM_event_tablet_data_default_set(wmTabletData *tablet_data)
4186 {
4187   *tablet_data = wm_event_tablet_data_default;
4188 }
4189 
wm_tablet_data_from_ghost(const GHOST_TabletData * tablet_data,wmTabletData * wmtab)4190 void wm_tablet_data_from_ghost(const GHOST_TabletData *tablet_data, wmTabletData *wmtab)
4191 {
4192   if ((tablet_data != NULL) && tablet_data->Active != GHOST_kTabletModeNone) {
4193     wmtab->active = (int)tablet_data->Active;
4194     wmtab->pressure = wm_pressure_curve(tablet_data->Pressure);
4195     wmtab->x_tilt = tablet_data->Xtilt;
4196     wmtab->y_tilt = tablet_data->Ytilt;
4197     /* We could have a preference to support relative tablet motion (we can't detect that). */
4198     wmtab->is_motion_absolute = true;
4199     // printf("%s: using tablet %.5f\n", __func__, wmtab->pressure);
4200   }
4201   else {
4202     *wmtab = wm_event_tablet_data_default;
4203     // printf("%s: not using tablet\n", __func__);
4204   }
4205 }
4206 
4207 #ifdef WITH_INPUT_NDOF
4208 /* Adds customdata to event. */
attach_ndof_data(wmEvent * event,const GHOST_TEventNDOFMotionData * ghost)4209 static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *ghost)
4210 {
4211   wmNDOFMotionData *data = MEM_mallocN(sizeof(wmNDOFMotionData), "customdata NDOF");
4212 
4213   const float ts = U.ndof_sensitivity;
4214   const float rs = U.ndof_orbit_sensitivity;
4215 
4216   mul_v3_v3fl(data->tvec, &ghost->tx, ts);
4217   mul_v3_v3fl(data->rvec, &ghost->rx, rs);
4218 
4219   if (U.ndof_flag & NDOF_PAN_YZ_SWAP_AXIS) {
4220     float t;
4221     t = data->tvec[1];
4222     data->tvec[1] = -data->tvec[2];
4223     data->tvec[2] = t;
4224   }
4225 
4226   data->dt = ghost->dt;
4227 
4228   data->progress = (wmProgress)ghost->progress;
4229 
4230   event->custom = EVT_DATA_NDOF_MOTION;
4231   event->customdata = data;
4232   event->customdatafree = 1;
4233 }
4234 #endif /* WITH_INPUT_NDOF */
4235 
4236 /* Imperfect but probably usable... draw/enable drags to other windows. */
wm_event_cursor_other_windows(wmWindowManager * wm,wmWindow * win,wmEvent * event)4237 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event)
4238 {
4239   int mval[2] = {event->x, event->y};
4240 
4241   if (wm->windows.first == wm->windows.last) {
4242     return NULL;
4243   }
4244 
4245   /* In order to use window size and mouse position (pixels), we have to use a WM function. */
4246 
4247   /* check if outside, include top window bar... */
4248   if (mval[0] < 0 || mval[1] < 0 || mval[0] > WM_window_pixels_x(win) ||
4249       mval[1] > WM_window_pixels_y(win) + 30) {
4250     /* Let's skip windows having modal handlers now */
4251     /* potential XXX ugly... I wouldn't have added a modalhandlers list
4252      * (introduced in rev 23331, ton). */
4253     LISTBASE_FOREACH (wmEventHandler *, handler, &win->modalhandlers) {
4254       if (ELEM(handler->type, WM_HANDLER_TYPE_UI, WM_HANDLER_TYPE_OP)) {
4255         return NULL;
4256       }
4257     }
4258 
4259     wmWindow *owin;
4260     if (WM_window_find_under_cursor(wm, win, win, mval, &owin, mval)) {
4261       event->x = mval[0];
4262       event->y = mval[1];
4263       return owin;
4264     }
4265   }
4266   return NULL;
4267 }
4268 
wm_event_is_double_click(const wmEvent * event,const wmEvent * event_state)4269 static bool wm_event_is_double_click(const wmEvent *event, const wmEvent *event_state)
4270 {
4271   if ((event->type == event_state->prevtype) && (event_state->prevval == KM_RELEASE) &&
4272       (event->val == KM_PRESS)) {
4273     if (ISMOUSE(event->type) && WM_event_drag_test(event, &event_state->prevclickx)) {
4274       /* Pass. */
4275     }
4276     else {
4277       if ((PIL_check_seconds_timer() - event_state->prevclicktime) * 1000 < U.dbl_click_time) {
4278         return true;
4279       }
4280     }
4281   }
4282 
4283   return false;
4284 }
4285 
wm_event_add_mousemove(wmWindow * win,const wmEvent * event)4286 static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
4287 {
4288   wmEvent *event_last = win->queue.last;
4289 
4290   /* Some painting operators want accurate mouse events, they can
4291    * handle in between mouse move moves, others can happily ignore
4292    * them for better performance. */
4293   if (event_last && event_last->type == MOUSEMOVE) {
4294     event_last->type = INBETWEEN_MOUSEMOVE;
4295   }
4296 
4297   wmEvent *event_new = wm_event_add(win, event);
4298   if (event_last == NULL) {
4299     event_last = win->eventstate;
4300   }
4301 
4302   copy_v2_v2_int(&event_new->prevx, &event_last->x);
4303   return event_new;
4304 }
4305 
4306 /* Windows store own event queues, no bContext here. */
4307 /* Time is in 1000s of seconds, from Ghost. */
wm_event_add_ghostevent(wmWindowManager * wm,wmWindow * win,int type,void * customdata)4308 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata)
4309 {
4310   if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
4311     return;
4312   }
4313 
4314   /**
4315    * Having both, \a event and \a evt, can be highly confusing to work with,
4316    * but is necessary for our current event system, so let's clear things up a bit:
4317    *
4318    * - Data added to event only will be handled immediately,
4319    *   but will not be copied to the next event.
4320    * - Data added to \a evt only stays,
4321    *   but is handled with the next event -> execution delay.
4322    * - Data added to event and \a evt stays and is handled immediately.
4323    */
4324   wmEvent event, *evt = win->eventstate;
4325 
4326   /* Initialize and copy state (only mouse x y and modifiers). */
4327   event = *evt;
4328   event.is_repeat = false;
4329 
4330   switch (type) {
4331     /* Mouse move, also to inactive window (X11 does this). */
4332     case GHOST_kEventCursorMove: {
4333       GHOST_TEventCursorData *cd = customdata;
4334 
4335       copy_v2_v2_int(&event.x, &cd->x);
4336       wm_stereo3d_mouse_offset_apply(win, &event.x);
4337       wm_tablet_data_from_ghost(&cd->tablet, &event.tablet);
4338 
4339       event.prevtype = event.type;
4340       event.prevval = event.val;
4341       event.type = MOUSEMOVE;
4342       {
4343         wmEvent *event_new = wm_event_add_mousemove(win, &event);
4344         copy_v2_v2_int(&evt->x, &event_new->x);
4345         evt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute;
4346       }
4347 
4348       /* Also add to other window if event is there, this makes overdraws disappear nicely. */
4349       /* It remaps mousecoord to other window in event. */
4350       wmWindow *owin = wm_event_cursor_other_windows(wm, win, &event);
4351       if (owin) {
4352         wmEvent oevent, *oevt = owin->eventstate;
4353 
4354         oevent = *oevt;
4355 
4356         copy_v2_v2_int(&oevent.x, &event.x);
4357         oevent.prevtype = oevent.type;
4358         oevent.prevval = oevent.val;
4359         oevent.type = MOUSEMOVE;
4360         {
4361           wmEvent *event_new = wm_event_add_mousemove(owin, &oevent);
4362           copy_v2_v2_int(&oevt->x, &event_new->x);
4363           oevt->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute;
4364         }
4365       }
4366 
4367       break;
4368     }
4369     case GHOST_kEventTrackpad: {
4370       GHOST_TEventTrackpadData *pd = customdata;
4371       switch (pd->subtype) {
4372         case GHOST_kTrackpadEventMagnify:
4373           event.type = MOUSEZOOM;
4374           pd->deltaX = -pd->deltaX;
4375           pd->deltaY = -pd->deltaY;
4376           break;
4377         case GHOST_kTrackpadEventSmartMagnify:
4378           event.type = MOUSESMARTZOOM;
4379           break;
4380         case GHOST_kTrackpadEventRotate:
4381           event.type = MOUSEROTATE;
4382           break;
4383         case GHOST_kTrackpadEventScroll:
4384         default:
4385           event.type = MOUSEPAN;
4386           break;
4387       }
4388 
4389       event.x = evt->x = pd->x;
4390       event.y = evt->y = pd->y;
4391       event.val = KM_NOTHING;
4392 
4393       /* Use prevx/prevy so we can calculate the delta later. */
4394       event.prevx = event.x - pd->deltaX;
4395       event.prevy = event.y - (-pd->deltaY);
4396 
4397       wm_event_add(win, &event);
4398       break;
4399     }
4400     /* ,ouse button, */
4401     case GHOST_kEventButtonDown:
4402     case GHOST_kEventButtonUp: {
4403       GHOST_TEventButtonData *bd = customdata;
4404 
4405       /* Get value and type from Ghost. */
4406       event.val = (type == GHOST_kEventButtonDown) ? KM_PRESS : KM_RELEASE;
4407 
4408       if (bd->button == GHOST_kButtonMaskLeft) {
4409         event.type = LEFTMOUSE;
4410       }
4411       else if (bd->button == GHOST_kButtonMaskRight) {
4412         event.type = RIGHTMOUSE;
4413       }
4414       else if (bd->button == GHOST_kButtonMaskButton4) {
4415         event.type = BUTTON4MOUSE;
4416       }
4417       else if (bd->button == GHOST_kButtonMaskButton5) {
4418         event.type = BUTTON5MOUSE;
4419       }
4420       else if (bd->button == GHOST_kButtonMaskButton6) {
4421         event.type = BUTTON6MOUSE;
4422       }
4423       else if (bd->button == GHOST_kButtonMaskButton7) {
4424         event.type = BUTTON7MOUSE;
4425       }
4426       else {
4427         event.type = MIDDLEMOUSE;
4428       }
4429 
4430       /* Get tablet data. */
4431       wm_tablet_data_from_ghost(&bd->tablet, &event.tablet);
4432 
4433       wm_eventemulation(&event, false);
4434 
4435       /* Copy previous state to prev event state (two old!). */
4436       evt->prevval = evt->val;
4437       evt->prevtype = evt->type;
4438 
4439       /* Copy to event state. */
4440       evt->val = event.val;
4441       evt->type = event.type;
4442 
4443       /* Double click test. */
4444       if (wm_event_is_double_click(&event, evt)) {
4445         CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
4446         event.val = KM_DBL_CLICK;
4447       }
4448       if (event.val == KM_PRESS) {
4449         evt->prevclicktime = PIL_check_seconds_timer();
4450         evt->prevclickx = event.x;
4451         evt->prevclicky = event.y;
4452       }
4453 
4454       /* Add to other window if event is there (not to both!). */
4455       wmWindow *owin = wm_event_cursor_other_windows(wm, win, &event);
4456       if (owin) {
4457         wmEvent oevent = *(owin->eventstate);
4458 
4459         oevent.x = event.x;
4460         oevent.y = event.y;
4461         oevent.type = event.type;
4462         oevent.val = event.val;
4463         oevent.tablet = event.tablet;
4464 
4465         wm_event_add(owin, &oevent);
4466       }
4467       else {
4468         wm_event_add(win, &event);
4469       }
4470 
4471       break;
4472     }
4473     /* Keyboard. */
4474     case GHOST_kEventKeyDown:
4475     case GHOST_kEventKeyUp: {
4476       GHOST_TEventKeyData *kd = customdata;
4477       short keymodifier = KM_NOTHING;
4478       event.type = convert_key(kd->key);
4479       event.ascii = kd->ascii;
4480       /* Might be not NULL terminated. */
4481       memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf));
4482       event.is_repeat = kd->is_repeat;
4483       event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE;
4484 
4485       wm_eventemulation(&event, false);
4486 
4487       /* Copy previous state to prev event state (two old!). */
4488       evt->prevval = evt->val;
4489       evt->prevtype = evt->type;
4490 
4491       /* Copy to event state. */
4492       evt->val = event.val;
4493       evt->type = event.type;
4494       evt->is_repeat = event.is_repeat;
4495 
4496       /* Exclude arrow keys, esc, etc from text input. */
4497       if (type == GHOST_kEventKeyUp) {
4498         event.ascii = '\0';
4499 
4500         /* Ghost should do this already for key up. */
4501         if (event.utf8_buf[0]) {
4502           CLOG_ERROR(WM_LOG_EVENTS,
4503                      "ghost on your platform is misbehaving, utf8 events on key up!");
4504         }
4505         event.utf8_buf[0] = '\0';
4506       }
4507       else {
4508         if (event.ascii < 32 && event.ascii > 0) {
4509           event.ascii = '\0';
4510         }
4511         if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) {
4512           event.utf8_buf[0] = '\0';
4513         }
4514       }
4515 
4516       if (event.utf8_buf[0]) {
4517         if (BLI_str_utf8_size(event.utf8_buf) == -1) {
4518           CLOG_ERROR(WM_LOG_EVENTS,
4519                      "ghost detected an invalid unicode character '%d'",
4520                      (int)(unsigned char)event.utf8_buf[0]);
4521           event.utf8_buf[0] = '\0';
4522         }
4523       }
4524 
4525       /* Assigning both first and second is strange. - campbell */
4526       switch (event.type) {
4527         case EVT_LEFTSHIFTKEY:
4528         case EVT_RIGHTSHIFTKEY:
4529           if (event.val == KM_PRESS) {
4530             if (evt->ctrl || evt->alt || evt->oskey) {
4531               keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
4532             }
4533             else {
4534               keymodifier = KM_MOD_FIRST;
4535             }
4536           }
4537           event.shift = evt->shift = keymodifier;
4538           break;
4539         case EVT_LEFTCTRLKEY:
4540         case EVT_RIGHTCTRLKEY:
4541           if (event.val == KM_PRESS) {
4542             if (evt->shift || evt->alt || evt->oskey) {
4543               keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
4544             }
4545             else {
4546               keymodifier = KM_MOD_FIRST;
4547             }
4548           }
4549           event.ctrl = evt->ctrl = keymodifier;
4550           break;
4551         case EVT_LEFTALTKEY:
4552         case EVT_RIGHTALTKEY:
4553           if (event.val == KM_PRESS) {
4554             if (evt->ctrl || evt->shift || evt->oskey) {
4555               keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
4556             }
4557             else {
4558               keymodifier = KM_MOD_FIRST;
4559             }
4560           }
4561           event.alt = evt->alt = keymodifier;
4562           break;
4563         case EVT_OSKEY:
4564           if (event.val == KM_PRESS) {
4565             if (evt->ctrl || evt->alt || evt->shift) {
4566               keymodifier = (KM_MOD_FIRST | KM_MOD_SECOND);
4567             }
4568             else {
4569               keymodifier = KM_MOD_FIRST;
4570             }
4571           }
4572           event.oskey = evt->oskey = keymodifier;
4573           break;
4574         default:
4575           if (event.val == KM_PRESS && event.keymodifier == 0) {
4576             /* Only set in eventstate, for next event. */
4577             evt->keymodifier = event.type;
4578           }
4579           else if (event.val == KM_RELEASE && event.keymodifier == event.type) {
4580             event.keymodifier = evt->keymodifier = 0;
4581           }
4582           break;
4583       }
4584 
4585       /* Double click test. */
4586       /* If previous event was same type, and previous was release, and now it presses... */
4587       if (wm_event_is_double_click(&event, evt)) {
4588         CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
4589         event.val = KM_DBL_CLICK;
4590       }
4591 
4592       /* This case happens on holding a key pressed, it should not generate
4593        * press events events with the same key as modifier. */
4594       if (event.keymodifier == event.type) {
4595         event.keymodifier = 0;
4596       }
4597 
4598       /* This case happens with an external numpad, and also when using 'dead keys'
4599        * (to compose complex latin characters e.g.), it's not really clear why.
4600        * Since it's impossible to map a key modifier to an unknown key,
4601        * it shouldn't harm to clear it. */
4602       if (event.keymodifier == EVT_UNKNOWNKEY) {
4603         evt->keymodifier = event.keymodifier = 0;
4604       }
4605 
4606       /* If test_break set, it catches this. Do not set with modifier presses.
4607        * XXX Keep global for now? */
4608       if ((event.type == EVT_ESCKEY && event.val == KM_PRESS) &&
4609           /* Check other modifiers because ms-windows uses these to bring up the task manager. */
4610           (event.shift == 0 && event.ctrl == 0 && event.alt == 0)) {
4611         G.is_break = true;
4612       }
4613 
4614       /* Double click test - only for press. */
4615       if (event.val == KM_PRESS) {
4616         /* Don't reset timer & location when holding the key generates repeat events. */
4617         if ((evt->prevtype != event.type) || (evt->prevval != KM_PRESS)) {
4618           evt->prevclicktime = PIL_check_seconds_timer();
4619           evt->prevclickx = event.x;
4620           evt->prevclicky = event.y;
4621         }
4622       }
4623 
4624       wm_event_add(win, &event);
4625 
4626       break;
4627     }
4628 
4629     case GHOST_kEventWheel: {
4630       GHOST_TEventWheelData *wheelData = customdata;
4631 
4632       if (wheelData->z > 0) {
4633         event.type = WHEELUPMOUSE;
4634       }
4635       else {
4636         event.type = WHEELDOWNMOUSE;
4637       }
4638 
4639       event.val = KM_PRESS;
4640       wm_event_add(win, &event);
4641 
4642       break;
4643     }
4644     case GHOST_kEventTimer: {
4645       event.type = TIMER;
4646       event.custom = EVT_DATA_TIMER;
4647       event.customdata = customdata;
4648       event.val = KM_NOTHING;
4649       event.keymodifier = 0;
4650       wm_event_add(win, &event);
4651 
4652       break;
4653     }
4654 
4655 #ifdef WITH_INPUT_NDOF
4656     case GHOST_kEventNDOFMotion: {
4657       event.type = NDOF_MOTION;
4658       event.val = KM_NOTHING;
4659       attach_ndof_data(&event, customdata);
4660       wm_event_add(win, &event);
4661 
4662       CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y);
4663       break;
4664     }
4665 
4666     case GHOST_kEventNDOFButton: {
4667       GHOST_TEventNDOFButtonData *e = customdata;
4668 
4669       event.type = NDOF_BUTTON_NONE + e->button;
4670 
4671       switch (e->action) {
4672         case GHOST_kPress:
4673           event.val = KM_PRESS;
4674           break;
4675         case GHOST_kRelease:
4676           event.val = KM_RELEASE;
4677           break;
4678       }
4679 
4680       event.custom = 0;
4681       event.customdata = NULL;
4682 
4683       wm_event_add(win, &event);
4684 
4685       break;
4686     }
4687 #endif /* WITH_INPUT_NDOF */
4688 
4689     case GHOST_kEventUnknown:
4690     case GHOST_kNumEventTypes:
4691       break;
4692 
4693     case GHOST_kEventWindowDeactivate: {
4694       event.type = WINDEACTIVATE;
4695       wm_event_add(win, &event);
4696 
4697       break;
4698     }
4699 
4700 #ifdef WITH_INPUT_IME
4701     case GHOST_kEventImeCompositionStart: {
4702       event.val = KM_PRESS;
4703       win->ime_data = customdata;
4704       win->ime_data->is_ime_composing = true;
4705       event.type = WM_IME_COMPOSITE_START;
4706       wm_event_add(win, &event);
4707       break;
4708     }
4709     case GHOST_kEventImeComposition: {
4710       event.val = KM_PRESS;
4711       event.type = WM_IME_COMPOSITE_EVENT;
4712       wm_event_add(win, &event);
4713       break;
4714     }
4715     case GHOST_kEventImeCompositionEnd: {
4716       event.val = KM_PRESS;
4717       if (win->ime_data) {
4718         win->ime_data->is_ime_composing = false;
4719       }
4720       event.type = WM_IME_COMPOSITE_END;
4721       wm_event_add(win, &event);
4722       break;
4723     }
4724 #endif /* WITH_INPUT_IME */
4725   }
4726 
4727 #if 0
4728   WM_event_print(&event);
4729 #endif
4730 }
4731 
4732 /** \} */
4733 
4734 /* -------------------------------------------------------------------- */
4735 /** \name WM Interface Locking
4736  * \{ */
4737 
4738 /**
4739  * Check whether operator is allowed to run in case interface is locked,
4740  * If interface is unlocked, will always return truth.
4741  */
wm_operator_check_locked_interface(bContext * C,wmOperatorType * ot)4742 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot)
4743 {
4744   wmWindowManager *wm = CTX_wm_manager(C);
4745 
4746   if (wm->is_interface_locked) {
4747     if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) {
4748       return false;
4749     }
4750   }
4751 
4752   return true;
4753 }
4754 
WM_set_locked_interface(wmWindowManager * wm,bool lock)4755 void WM_set_locked_interface(wmWindowManager *wm, bool lock)
4756 {
4757   /* This will prevent events from being handled while interface is locked
4758    *
4759    * Use a "local" flag for now, because currently no other areas could
4760    * benefit of locked interface anyway (aka using G.is_interface_locked
4761    * wouldn't be useful anywhere outside of window manager, so let's not
4762    * pollute global context with such an information for now).
4763    */
4764   wm->is_interface_locked = lock ? 1 : 0;
4765 
4766   /* This will prevent drawing regions which uses non-threadsafe data.
4767    * Currently it'll be just a 3D viewport.
4768    *
4769    * TODO(sergey): Make it different locked states, so different jobs
4770    *               could lock different areas of blender and allow
4771    *               interaction with others?
4772    */
4773   BKE_spacedata_draw_locks(lock);
4774 }
4775 
4776 /** \} */
4777 
4778 /* -------------------------------------------------------------------- */
4779 /** \name Event / Keymap Matching API
4780  * \{ */
4781 
WM_event_get_keymap_from_handler(wmWindowManager * wm,wmEventHandler_Keymap * handler)4782 wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, wmEventHandler_Keymap *handler)
4783 {
4784   wmKeyMap *keymap;
4785   if (handler->dynamic.keymap_fn != NULL) {
4786     keymap = handler->dynamic.keymap_fn(wm, handler);
4787     BLI_assert(handler->keymap == NULL);
4788   }
4789   else {
4790     keymap = WM_keymap_active(wm, handler->keymap);
4791     BLI_assert(keymap != NULL);
4792   }
4793   return keymap;
4794 }
4795 
WM_event_match_keymap_item(bContext * C,wmKeyMap * keymap,const wmEvent * event)4796 wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wmEvent *event)
4797 {
4798   LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
4799     if (wm_eventmatch(event, kmi)) {
4800       wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
4801       if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) {
4802         return kmi;
4803       }
4804     }
4805   }
4806   return NULL;
4807 }
4808 
WM_event_match_keymap_item_from_handlers(bContext * C,wmWindowManager * wm,ListBase * handlers,const wmEvent * event)4809 wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C,
4810                                                        wmWindowManager *wm,
4811                                                        ListBase *handlers,
4812                                                        const wmEvent *event)
4813 {
4814   LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
4815     /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
4816     if (handler_base->flag & WM_HANDLER_DO_FREE) {
4817       /* Pass. */
4818     }
4819     else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) {
4820       if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
4821         wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
4822         wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
4823         if (keymap && WM_keymap_poll(C, keymap)) {
4824           wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event);
4825           if (kmi != NULL) {
4826             return kmi;
4827           }
4828         }
4829       }
4830     }
4831   }
4832   return NULL;
4833 }
4834 
4835 /** \} */
4836 
4837 /* -------------------------------------------------------------------- */
4838 /** \name Cursor Keymap Status
4839  *
4840  * Show cursor keys in the status bar.
4841  * This is done by detecting changes to the state - full keymap lookups are expensive
4842  * so only perform this on changing tools, space types, pressing different modifier keys... etc.
4843  * \{ */
4844 
4845 /** State storage to detect changes between calls to refresh the information. */
4846 struct CursorKeymapInfo_State {
4847   struct {
4848     short shift, ctrl, alt, oskey;
4849   } modifiers;
4850   short space_type;
4851   short region_type;
4852   /* Never use, just compare memory for changes. */
4853   bToolRef tref;
4854 };
4855 
4856 struct CursorKeymapInfo {
4857   /* 0: mouse button index
4858    * 1: event type (click/press, drag)
4859    * 2: text.
4860    */
4861   char text[3][2][128];
4862   wmEvent state_event;
4863   struct CursorKeymapInfo_State state;
4864 };
4865 
wm_event_cursor_store(struct CursorKeymapInfo_State * state,const wmEvent * event,short space_type,short region_type,const bToolRef * tref)4866 static void wm_event_cursor_store(struct CursorKeymapInfo_State *state,
4867                                   const wmEvent *event,
4868                                   short space_type,
4869                                   short region_type,
4870                                   const bToolRef *tref)
4871 {
4872   state->modifiers.shift = event->shift;
4873   state->modifiers.ctrl = event->ctrl;
4874   state->modifiers.alt = event->alt;
4875   state->modifiers.oskey = event->oskey;
4876   state->space_type = space_type;
4877   state->region_type = region_type;
4878   state->tref = tref ? *tref : (bToolRef){0};
4879 }
4880 
WM_window_cursor_keymap_status_get(const wmWindow * win,int button_index,int type_index)4881 const char *WM_window_cursor_keymap_status_get(const wmWindow *win,
4882                                                int button_index,
4883                                                int type_index)
4884 {
4885   if (win->cursor_keymap_status != NULL) {
4886     struct CursorKeymapInfo *cd = win->cursor_keymap_status;
4887     const char *msg = cd->text[button_index][type_index];
4888     if (*msg) {
4889       return msg;
4890     }
4891   }
4892   return NULL;
4893 }
4894 
4895 /**
4896  * Similar to #BKE_screen_area_map_find_area_xy and related functions,
4897  * use here since the area is stored in the window manager.
4898  */
WM_window_status_area_find(wmWindow * win,bScreen * screen)4899 ScrArea *WM_window_status_area_find(wmWindow *win, bScreen *screen)
4900 {
4901   if (screen->state == SCREENFULL) {
4902     return NULL;
4903   }
4904   ScrArea *area_statusbar = NULL;
4905   LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) {
4906     if (area->spacetype == SPACE_STATUSBAR) {
4907       area_statusbar = area;
4908       break;
4909     }
4910   }
4911   return area_statusbar;
4912 }
4913 
WM_window_status_area_tag_redraw(wmWindow * win)4914 void WM_window_status_area_tag_redraw(wmWindow *win)
4915 {
4916   bScreen *screen = WM_window_get_active_screen(win);
4917   ScrArea *area = WM_window_status_area_find(win, screen);
4918   if (area != NULL) {
4919     ED_area_tag_redraw(area);
4920   }
4921 }
4922 
WM_window_cursor_keymap_status_refresh(bContext * C,wmWindow * win)4923 void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
4924 {
4925   bScreen *screen = WM_window_get_active_screen(win);
4926   ScrArea *area_statusbar = WM_window_status_area_find(win, screen);
4927   if (area_statusbar == NULL) {
4928     MEM_SAFE_FREE(win->cursor_keymap_status);
4929     return;
4930   }
4931 
4932   struct CursorKeymapInfo *cd;
4933   if (UNLIKELY(win->cursor_keymap_status == NULL)) {
4934     win->cursor_keymap_status = MEM_callocN(sizeof(struct CursorKeymapInfo), __func__);
4935   }
4936   cd = win->cursor_keymap_status;
4937 
4938   /* Detect unchanged state (early exit). */
4939   if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) {
4940     return;
4941   }
4942 
4943   /* Now perform more comprehensive check,
4944    * still keep this fast since it happens on mouse-move. */
4945   struct CursorKeymapInfo cd_prev = *((struct CursorKeymapInfo *)win->cursor_keymap_status);
4946   cd->state_event = *win->eventstate;
4947 
4948   /* Find active region and associated area. */
4949   ARegion *region = screen->active_region;
4950   if (region == NULL) {
4951     return;
4952   }
4953 
4954   ScrArea *area = NULL;
4955   ED_screen_areas_iter (win, screen, area_iter) {
4956     if (BLI_findindex(&area_iter->regionbase, region) != -1) {
4957       area = area_iter;
4958       break;
4959     }
4960   }
4961   if (area == NULL) {
4962     return;
4963   }
4964 
4965   /* Keep as-is. */
4966   if (ELEM(area->spacetype, SPACE_STATUSBAR, SPACE_TOPBAR)) {
4967     return;
4968   }
4969   if (ELEM(region->regiontype,
4970            RGN_TYPE_HEADER,
4971            RGN_TYPE_TOOL_HEADER,
4972            RGN_TYPE_FOOTER,
4973            RGN_TYPE_TEMPORARY,
4974            RGN_TYPE_HUD)) {
4975     return;
4976   }
4977   /* Fallback to window. */
4978   if (ELEM(region->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) {
4979     region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
4980   }
4981 
4982   /* Detect changes to the state. */
4983   {
4984     bToolRef *tref = NULL;
4985     if ((region->regiontype == RGN_TYPE_WINDOW) &&
4986         ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK)) {
4987       ViewLayer *view_layer = WM_window_get_active_view_layer(win);
4988       WorkSpace *workspace = WM_window_get_active_workspace(win);
4989       const bToolKey tkey = {
4990           .space_type = area->spacetype,
4991           .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
4992       };
4993       tref = WM_toolsystem_ref_find(workspace, &tkey);
4994     }
4995     wm_event_cursor_store(&cd->state, win->eventstate, area->spacetype, region->regiontype, tref);
4996     if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) {
4997       return;
4998     }
4999   }
5000 
5001   /* Changed context found, detect changes to keymap and refresh the status bar. */
5002   const struct {
5003     int button_index;
5004     int type_index; /* 0: press or click, 1: drag. */
5005     int event_type;
5006     int event_value;
5007   } event_data[] = {
5008       {0, 0, LEFTMOUSE, KM_PRESS},
5009       {0, 0, LEFTMOUSE, KM_CLICK},
5010       {0, 1, EVT_TWEAK_L, KM_ANY},
5011 
5012       {1, 0, MIDDLEMOUSE, KM_PRESS},
5013       {1, 0, MIDDLEMOUSE, KM_CLICK},
5014       {1, 1, EVT_TWEAK_M, KM_ANY},
5015 
5016       {2, 0, RIGHTMOUSE, KM_PRESS},
5017       {2, 0, RIGHTMOUSE, KM_CLICK},
5018       {2, 1, EVT_TWEAK_R, KM_ANY},
5019   };
5020 
5021   for (int button_index = 0; button_index < 3; button_index++) {
5022     cd->text[button_index][0][0] = '\0';
5023     cd->text[button_index][1][0] = '\0';
5024   }
5025 
5026   CTX_wm_window_set(C, win);
5027   CTX_wm_area_set(C, area);
5028   CTX_wm_region_set(C, region);
5029 
5030   ListBase *handlers[] = {
5031       &region->handlers,
5032       &area->handlers,
5033       &win->handlers,
5034   };
5035 
5036   wmWindowManager *wm = CTX_wm_manager(C);
5037   for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) {
5038     const int button_index = event_data[data_index].button_index;
5039     const int type_index = event_data[data_index].type_index;
5040     if (cd->text[button_index][type_index][0] != 0) {
5041       continue;
5042     }
5043     wmEvent test_event = *win->eventstate;
5044     test_event.type = event_data[data_index].event_type;
5045     test_event.val = event_data[data_index].event_value;
5046     wm_eventemulation(&test_event, true);
5047     wmKeyMapItem *kmi = NULL;
5048     for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
5049       kmi = WM_event_match_keymap_item_from_handlers(C, wm, handlers[handler_index], &test_event);
5050       if (kmi) {
5051         break;
5052       }
5053     }
5054     if (kmi) {
5055       wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
5056       const char *name = (ot) ? WM_operatortype_name(ot, kmi->ptr) : kmi->idname;
5057       STRNCPY(cd->text[button_index][type_index], name);
5058     }
5059   }
5060 
5061   if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) {
5062     ED_area_tag_redraw(area_statusbar);
5063   }
5064 
5065   CTX_wm_window_set(C, NULL);
5066 }
5067 
5068 /** \} */
5069 
5070 /* -------------------------------------------------------------------- */
5071 /** \name Modal Keymap Status
5072  *
5073  * \{ */
5074 
WM_window_modal_keymap_status_draw(bContext * C,wmWindow * win,uiLayout * layout)5075 bool WM_window_modal_keymap_status_draw(bContext *C, wmWindow *win, uiLayout *layout)
5076 {
5077   wmWindowManager *wm = CTX_wm_manager(C);
5078   wmKeyMap *keymap = NULL;
5079   wmOperator *op = NULL;
5080   LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
5081     if (handler_base->type == WM_HANDLER_TYPE_OP) {
5082       wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
5083       if (handler->op != NULL) {
5084         /* 'handler->keymap' could be checked too, seems not to be used. */
5085         wmKeyMap *keymap_test = WM_keymap_active(wm, handler->op->type->modalkeymap);
5086         if (keymap_test && keymap_test->modal_items) {
5087           keymap = keymap_test;
5088           op = handler->op;
5089           break;
5090         }
5091       }
5092     }
5093   }
5094   if (keymap == NULL || keymap->modal_items == NULL) {
5095     return false;
5096   }
5097   const EnumPropertyItem *items = keymap->modal_items;
5098 
5099   uiLayout *row = uiLayoutRow(layout, true);
5100   for (int i = 0; items[i].identifier; i++) {
5101     if (!items[i].identifier[0]) {
5102       continue;
5103     }
5104     if ((keymap->poll_modal_item != NULL) &&
5105         (keymap->poll_modal_item(op, items[i].value) == false)) {
5106       continue;
5107     }
5108 
5109     bool show_text = true;
5110 
5111     {
5112       /* Warning: O(n^2). */
5113       wmKeyMapItem *kmi = NULL;
5114       for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
5115         if (kmi->propvalue == items[i].value) {
5116           break;
5117         }
5118       }
5119       if (kmi != NULL) {
5120         if (kmi->val == KM_RELEASE) {
5121           /* Assume release events just disable something which was toggled on. */
5122           continue;
5123         }
5124         if (uiTemplateEventFromKeymapItem(row, items[i].name, kmi, false)) {
5125           show_text = false;
5126         }
5127       }
5128     }
5129     if (show_text) {
5130       char buf[UI_MAX_DRAW_STR];
5131       int available_len = sizeof(buf);
5132       char *p = buf;
5133       WM_modalkeymap_operator_items_to_string_buf(
5134           op->type, items[i].value, true, UI_MAX_SHORTCUT_STR, &available_len, &p);
5135       p -= 1;
5136       if (p > buf) {
5137         BLI_snprintf(p, available_len, ": %s", items[i].name);
5138         uiItemL(row, buf, 0);
5139       }
5140     }
5141   }
5142   return true;
5143 }
5144 
5145 /** \} */
5146