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, ®ion->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(®ion->winrct, &event->x)) {
1364 winrect = ®ion->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(®ion->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(®ion->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, ®ion->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 ®ion->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