Blender V4.5
wm_event_system.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
12
13#include <cstdlib>
14#include <cstring>
15#include <fmt/format.h>
16
17#include "AS_asset_library.hh"
18
19#include "DNA_listBase.h"
20#include "DNA_scene_types.h"
21#include "DNA_screen_types.h"
22#include "DNA_userdef_types.h"
24
25#include "MEM_guardedalloc.h"
26
27#include "CLG_log.h"
28
29#include "GHOST_C-api.h"
30
31#include "BLI_ghash.h"
32#include "BLI_listbase.h"
33#include "BLI_math_vector.h"
34#include "BLI_string.h"
35#include "BLI_string_utf8.h"
36#include "BLI_timer.h"
37#include "BLI_utildefines.h"
38
39#include "BKE_context.hh"
40#include "BKE_customdata.hh"
41#include "BKE_global.hh"
42#include "BKE_idprop.hh"
43#include "BKE_lib_remap.hh"
44#include "BKE_library.hh"
45#include "BKE_main.hh"
46#include "BKE_report.hh"
47#include "BKE_scene.hh"
48#include "BKE_screen.hh"
49#include "BKE_undo_system.hh"
50#include "BKE_workspace.hh"
51
52#include "BKE_sound.h"
53
54#include "BLT_translation.hh"
55
56#include "ED_asset.hh"
57#include "ED_fileselect.hh"
58#include "ED_info.hh"
59#include "ED_markers.hh"
60#include "ED_render.hh"
61#include "ED_screen.hh"
62#include "ED_undo.hh"
63#include "ED_util.hh"
64#include "ED_view3d.hh"
65
66#include "GPU_context.hh"
67
68#include "RNA_access.hh"
69
70#include "UI_interface.hh"
71#include "UI_view2d.hh"
72
73#include "WM_api.hh"
74#include "WM_keymap.hh"
75#include "WM_message.hh"
76#include "WM_toolsystem.hh"
77#include "WM_types.hh"
78
79#include "wm.hh"
80#include "wm_event_system.hh"
81#include "wm_event_types.hh"
82#include "wm_surface.hh"
83#include "wm_window.hh"
84#include "wm_window_private.hh"
85
86#include "DEG_depsgraph.hh"
88
89#include "RE_pipeline.h"
90
92
105#define USE_GIZMO_MOUSE_PRIORITY_HACK
106
107#ifdef WITH_INPUT_IME
108BLI_STATIC_ASSERT(sizeof(GHOST_TEventImeData) == sizeof(wmIMEData),
109 "These structs must match exactly!");
110#endif
111
123#define WM_HANDLER_CONTINUE ((eHandlerActionFlag)0)
124
125static void wm_notifier_clear(wmNotifier *note);
126static bool wm_notifier_is_clear(const wmNotifier *note);
127
130 PointerRNA *properties,
132 const wmOperatorCallContext context,
133 const bool poll_only,
134 const wmEvent *event);
135
138static void wm_operator_free_for_fileselect(wmOperator *file_operator);
139
141 uint64_t event_time_ms,
142 wmEvent *event_state,
143 uint64_t *event_state_prev_press_time_ms_p,
144 const bool is_keyboard,
145 const bool check_double_click);
146
147/* -------------------------------------------------------------------- */
150
155static bool screen_temp_region_exists(const ARegion *region)
156{
157 /* TODO(@ideasman42): this function would ideally not be needed.
158 * It avoids problems restoring the #bContext::wm::region_popup
159 * when it's not known if the popup was removed, however it would be better to
160 * resolve this by ensuring the contexts previous state never references stale data.
161 *
162 * This could be done using a context "stack" allowing freeing windowing data to clear
163 * references at all levels in the stack. */
164
165 Main *bmain = G_MAIN;
166 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
167 if (BLI_findindex(&screen->regionbase, region) != -1) {
168 return true;
169 }
170 }
171 return false;
172}
173
175
176/* -------------------------------------------------------------------- */
179
180static wmEvent *wm_event_add_intern(wmWindow *win, const wmEvent *event_to_add)
181{
182 wmEvent *event = MEM_callocN<wmEvent>(__func__);
183
184 *event = *event_to_add;
185
186 BLI_addtail(&win->runtime->event_queue, event);
187 return event;
188}
189
190wmEvent *WM_event_add(wmWindow *win, const wmEvent *event_to_add)
191{
192 return wm_event_add_intern(win, event_to_add);
193}
194
196{
197 if ((G.f & G_FLAG_EVENT_SIMULATE) == 0) {
199 return nullptr;
200 }
201 wmEvent *event = WM_event_add(win, event_to_add);
202
203 /* Logic for setting previous value is documented on the #wmEvent struct,
204 * see #wm_event_add_ghostevent for the implementation of logic this follows. */
205 copy_v2_v2_int(win->eventstate->xy, event->xy);
206
207 if (event->type == MOUSEMOVE) {
209 copy_v2_v2_int(event->prev_xy, win->eventstate->xy);
210 }
211 else if (ISKEYBOARD_OR_BUTTON(event->type)) {
212 /* Dummy time for simulated events. */
213 const uint64_t event_time_ms = UINT64_MAX;
214 uint64_t eventstate_prev_press_time_ms = 0;
216 event_time_ms,
217 win->eventstate,
218 &eventstate_prev_press_time_ms,
219 ISKEYBOARD(event->type),
220 false);
221 }
222 return event;
223}
224
225static void wm_event_custom_free(wmEvent *event)
226{
227 if ((event->customdata && event->customdata_free) == 0) {
228 return;
229 }
230
231 /* NOTE: pointer to #ListBase struct elsewhere. */
232 if (event->custom == EVT_DATA_DRAGDROP) {
233 ListBase *lb = static_cast<ListBase *>(event->customdata);
235 }
236 else {
237 MEM_freeN(event->customdata);
238 }
239}
240
242{
243 event->custom = 0;
244 event->customdata = nullptr;
245 event->customdata_free = false;
246}
247
249{
250#ifndef NDEBUG
251 /* Don't use assert here because it's fairly harmless in most cases,
252 * more an issue of correctness, something we should avoid in general. */
253 if ((event->flag & WM_EVENT_IS_REPEAT) && !ISKEYBOARD(event->type)) {
254 printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__);
255 WM_event_print(event);
256 }
257 if (ISMOUSE_MOTION(event->type) && (event->val != KM_NOTHING)) {
258 printf("%s: 'val != NOTHING' for a cursor motion event, this should not happen.\n", __func__);
259 WM_event_print(event);
260 }
261#endif
262
264
265 MEM_freeN(event);
266}
267
270{
271 /* Don't rely on this pointer being valid,
272 * callers should behave as if the memory has been freed.
273 * As this function should be interchangeable with #wm_event_free. */
274#ifndef NDEBUG
275 {
276 wmEvent *event_copy = static_cast<wmEvent *>(MEM_dupallocN(event));
277 MEM_freeN(event);
278 event = event_copy;
279 }
280#endif
281
282 if (win->event_last_handled) {
284 }
285
286 /* While not essential, these values are undefined, as the event is no longer in a list
287 * clear the linked-list pointers to avoid any confusion. */
288 event->next = event->prev = nullptr;
289
290 /* Don't store custom data in the last handled event as we don't have control how long this event
291 * will be stored and the referenced data may become invalid (also it's not needed currently). */
294 win->event_last_handled = event;
295}
296
298{
299 wmEvent *event = static_cast<wmEvent *>(BLI_poptail(&win->runtime->event_queue));
300 if (event != nullptr) {
301 wm_event_free(event);
302 }
303}
304
306{
307 while (wmEvent *event = static_cast<wmEvent *>(BLI_pophead(&win->runtime->event_queue))) {
308 wm_event_free(event);
309 }
310}
311
313{
314 *event = *(win->eventstate);
315}
316
318
319/* -------------------------------------------------------------------- */
322
326static uint note_hash_for_queue_fn(const void *ptr)
327{
328 const wmNotifier *note = static_cast<const wmNotifier *>(ptr);
329 return (BLI_ghashutil_ptrhash(note->reference) ^
330 (note->category | note->data | note->subtype | note->action));
331}
332
338static bool note_cmp_for_queue_fn(const void *a, const void *b)
339{
340 const wmNotifier *note_a = static_cast<const wmNotifier *>(a);
341 const wmNotifier *note_b = static_cast<const wmNotifier *>(b);
342 return !(((note_a->category | note_a->data | note_a->subtype | note_a->action) ==
343 (note_b->category | note_b->data | note_b->subtype | note_b->action)) &&
344 (note_a->reference == note_b->reference));
345}
346
348 const wmWindow *win,
349 uint type,
350 void *reference)
351{
352 BLI_assert(wm != nullptr);
353
354 wmNotifier note_test = {nullptr};
355
356 note_test.window = win;
357
358 note_test.category = type & NOTE_CATEGORY;
359 note_test.data = type & NOTE_DATA;
360 note_test.subtype = type & NOTE_SUBTYPE;
361 note_test.action = type & NOTE_ACTION;
362 note_test.reference = reference;
363
364 BLI_assert(!wm_notifier_is_clear(&note_test));
365
366 if (wm->runtime->notifier_queue_set == nullptr) {
367 wm->runtime->notifier_queue_set = BLI_gset_new_ex(
369 }
370
371 void **note_p;
372 if (BLI_gset_ensure_p_ex(wm->runtime->notifier_queue_set, &note_test, &note_p)) {
373 return;
374 }
375 wmNotifier *note = MEM_callocN<wmNotifier>(__func__);
376 *note = note_test;
377 *note_p = note;
378 BLI_addtail(&wm->runtime->notifier_queue, note);
379}
380
381void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
382{
383 if (wm == nullptr) {
384 /* There may be some cases where e.g. `G_MAIN` is not actually the real current main, but some
385 * other temporary one (e.g. during liboverride processing over linked data), leading to null
386 * window manager.
387 *
388 * This is fairly bad and weak, but unfortunately RNA does not have any way to operate over
389 * another main than G_MAIN currently. */
390 return;
391 }
392 wm_event_add_notifier_intern(wm, win, type, reference);
393}
394
395void WM_event_add_notifier(const bContext *C, uint type, void *reference)
396{
397 /* XXX: in future, which notifiers to send to other windows? */
398
400}
401
402void WM_main_add_notifier(uint type, void *reference)
403{
404 Main *bmain = G_MAIN;
405 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
406
407 WM_event_add_notifier_ex(wm, nullptr, type, reference);
408}
409
410void WM_main_remove_notifier_reference(const void *reference)
411{
412 Main *bmain = G_MAIN;
413 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
414
415 if (wm) {
416 LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->runtime->notifier_queue) {
417 if (note->reference == reference) {
418 const bool removed = BLI_gset_remove(wm->runtime->notifier_queue_set, note, nullptr);
419 BLI_assert(removed);
420 UNUSED_VARS_NDEBUG(removed);
421
422 /* Remove unless this is being iterated over by the caller.
423 * This is done to prevent `wm->runtime->notifier_queue` accumulating notifiers
424 * that aren't handled which can happen when notifiers are added from Python scripts.
425 * see #129323. */
426 if (wm->runtime->notifier_current == note) {
427 /* Don't remove because this causes problems for #wm_event_do_notifiers
428 * which may be looping on the data (deleting screens). */
429 wm_notifier_clear(note);
430 }
431 else {
432 BLI_remlink(&wm->runtime->notifier_queue, note);
433 MEM_freeN(note);
434 }
435 }
436 }
437
438 /* Remap instead. */
439#if 0
440 if (wm->message_bus) {
441 WM_msg_id_remove(wm->message_bus, reference);
442 }
443#endif
444 }
445}
446
448{
449 Main *bmain = G_MAIN;
450
451 LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
452 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
453 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
454 ED_spacedata_id_remap(area, sl, mappings);
455 }
456 }
457 }
458
459 mappings.iter(
460 [](ID *old_id, ID *new_id) { blender::ed::asset::list::storage_id_remap(old_id, new_id); });
461
462 if (wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first)) {
463 if (wmMsgBus *mbus = wm->message_bus) {
464 mappings.iter([&](ID *old_id, ID *new_id) {
465 if (new_id != nullptr) {
466 WM_msg_id_update(mbus, old_id, new_id);
467 }
468 else {
469 WM_msg_id_remove(mbus, old_id);
470 }
471 });
472 }
473 }
474
476}
477
479{
480 /* Clear the entire notifier, only leaving (`next`, `prev`) members intact. */
481 memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
483}
484
485static bool wm_notifier_is_clear(const wmNotifier *note)
486{
487 return note->category == NOTE_CATEGORY_TAG_CLEARED;
488}
489
490void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
491{
493 /* The whole idea of locked interface is to prevent viewport and whatever thread from
494 * modifying the same data. Because of this, we can not perform dependency graph update. */
495 if (wm->runtime->is_interface_locked) {
496 return;
497 }
498 /* Combine data-masks so one window doesn't disable UVs in another #26448. */
499 CustomData_MeshMasks win_combine_v3d_datamask = {0};
500 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
501 const Scene *scene = WM_window_get_active_scene(win);
503 const bScreen *screen = WM_window_get_active_screen(win);
504
505 ED_view3d_screen_datamask(scene, view_layer, screen, &win_combine_v3d_datamask);
506 }
507 /* Update all the dependency graphs of visible view layers. */
508 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
509 Scene *scene = WM_window_get_active_scene(win);
511 Main *bmain = CTX_data_main(C);
512 /* Copied to set's in #scene_update_tagged_recursive(). */
513 scene->customdata_mask = win_combine_v3d_datamask;
514 /* XXX, hack so operators can enforce data-masks #26482, GPU render. */
516 /* TODO(sergey): For now all dependency graphs which are evaluated from
517 * workspace are considered active. This will work all fine with "locked"
518 * view layer and time across windows. This is to be granted separately,
519 * and for until then we have to accept ambiguities when object is shared
520 * across visible view layers and has overrides on it. */
521 Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
522 if (is_after_open_file) {
524 }
527 }
528
530}
531
533{
535 /* Cached: editor refresh callbacks now, they get context. */
536 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
537 const bScreen *screen = WM_window_get_active_screen(win);
538
539 CTX_wm_window_set(C, win);
540 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
541 if (area->do_refresh) {
542 CTX_wm_area_set(C, area);
543 ED_area_do_refresh(C, area);
544 }
545 }
546 }
547
548 wm_event_do_depsgraph(C, false);
549
550 CTX_wm_window_set(C, nullptr);
551}
552
554{
556 if (UNLIKELY(wm == nullptr)) {
557 return;
558 }
559
560 /* Set the first window as context, so that there is some minimal context. This avoids crashes
561 * when calling code that assumes that there is always a window in the context (which many
562 * operators do). */
563 CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
565 CTX_wm_window_set(C, nullptr);
566}
567
569{
570 /* Ensure inside render boundary. */
572
573 /* Run the timer before assigning `wm` in the unlikely case a timer loads a file, see #80028. */
575
577 if (wm == nullptr) {
579 return;
580 }
581
582 /* Disable? - Keep for now since its used for window level notifiers. */
583#if 1
584 /* Cache & catch WM level notifiers, such as frame change, scene/screen set. */
585 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
586 Scene *scene = WM_window_get_active_scene(win);
587 bool do_anim = false;
588 bool clear_info_stats = false;
589
590 CTX_wm_window_set(C, win);
591
592 BLI_assert(wm->runtime->notifier_current == nullptr);
593 for (const wmNotifier *
594 note = static_cast<const wmNotifier *>(wm->runtime->notifier_queue.first),
595 *note_next = nullptr;
596 note;
597 note = note_next)
598 {
599 if (wm_notifier_is_clear(note)) {
600 note_next = note->next;
601 MEM_freeN(note);
602 continue;
603 }
604
605 wm->runtime->notifier_current = note;
606
607 if (note->category == NC_WM) {
608 if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
609 wm->file_saved = 1;
610 WM_window_title(wm, win);
611 }
612 else if (note->data == ND_DATACHANGED) {
613 WM_window_title(wm, win);
614 }
615 else if (note->data == ND_UNDO) {
617 }
618 }
619 if (note->window == win) {
620 if (note->category == NC_SCREEN) {
621 if (note->data == ND_WORKSPACE_SET) {
622 WorkSpace *ref_ws = static_cast<WorkSpace *>(note->reference);
623
624 UI_popup_handlers_remove_all(C, &win->modalhandlers);
625
626 WM_window_set_active_workspace(C, win, ref_ws);
627 if (G.debug & G_DEBUG_EVENTS) {
628 printf("%s: Workspace set %p\n", __func__, note->reference);
629 }
630 }
631 else if (note->data == ND_WORKSPACE_DELETE) {
632 WorkSpace *workspace = static_cast<WorkSpace *>(note->reference);
633
635 workspace, CTX_data_main(C), C, wm); /* XXX: hum, think this over! */
636 if (G.debug & G_DEBUG_EVENTS) {
637 printf("%s: Workspace delete %p\n", __func__, workspace);
638 }
639 }
640 else if (note->data == ND_LAYOUTBROWSE) {
642 static_cast<WorkSpaceLayout *>(note->reference));
643
644 /* Free popup handlers only #35434. */
645 UI_popup_handlers_remove_all(C, &win->modalhandlers);
646
647 ED_screen_change(C, ref_screen); /* XXX: hum, think this over! */
648 if (G.debug & G_DEBUG_EVENTS) {
649 printf("%s: screen set %p\n", __func__, note->reference);
650 }
651 }
652 else if (note->data == ND_LAYOUTDELETE) {
654 WorkSpaceLayout *layout = static_cast<WorkSpaceLayout *>(note->reference);
655
656 ED_workspace_layout_delete(workspace, layout, C); /* XXX: hum, think this over! */
657 if (G.debug & G_DEBUG_EVENTS) {
658 printf("%s: screen delete %p\n", __func__, note->reference);
659 }
660 }
661 }
662 }
663
664 if (note->window == win ||
665 (note->window == nullptr && ELEM(note->reference, nullptr, scene)))
666 {
667 if (note->category == NC_SCENE) {
668 if (note->data == ND_FRAME) {
669 do_anim = true;
670 }
671 }
672 }
673 if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
674 clear_info_stats = true;
675 }
676
677 wm->runtime->notifier_current = nullptr;
678
679 note_next = note->next;
680 if (wm_notifier_is_clear(note)) {
681 BLI_remlink(&wm->runtime->notifier_queue, (void *)note);
682 MEM_freeN(note);
683 }
684 }
685
686 if (clear_info_stats) {
687 /* Only do once since adding notifiers is slow when there are many. */
688 ViewLayer *view_layer = CTX_data_view_layer(C);
689 ED_info_stats_clear(wm, view_layer);
691 }
692
693 if (do_anim) {
694
695 /* XXX: quick frame changes can cause a crash if frame-change and rendering
696 * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
697 * twice which can depsgraph update the same object at once. */
698 if (G.is_rendering == false) {
699 /* Depsgraph gets called, might send more notifiers. */
702 }
703 }
704 }
705
706 BLI_assert(wm->runtime->notifier_current == nullptr);
707
708 /* The notifiers are sent without context, to keep it clean. */
709 while (const wmNotifier *note = static_cast<const wmNotifier *>(
710 BLI_pophead(&wm->runtime->notifier_queue)))
711 {
712 if (wm_notifier_is_clear(note)) {
713 MEM_freeN(note);
714 continue;
715 }
716 /* NOTE: no need to set `wm->runtime->notifier_current` since it's been removed from the queue.
717 */
718
719 const bool removed = BLI_gset_remove(wm->runtime->notifier_queue_set, note, nullptr);
720 BLI_assert(removed);
721 UNUSED_VARS_NDEBUG(removed);
722 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
723 Scene *scene = WM_window_get_active_scene(win);
726
727 /* Filter out notifiers. */
728 if (note->category == NC_SCREEN && note->reference && note->reference != screen &&
729 note->reference != workspace && note->reference != WM_window_get_active_layout(win))
730 {
731 /* Pass. */
732 }
733 else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
734 /* Pass. */
735 }
736 else {
737 /* XXX context in notifiers? */
738 CTX_wm_window_set(C, win);
739
740# if 0
741 printf("notifier win %d screen %s cat %x\n",
742 win->winid,
743 win->screen->id.name + 2,
744 note->category);
745# endif
747 ED_screen_do_listen(C, note);
748
749 LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
750 wmRegionListenerParams region_params{};
751 region_params.window = win;
752 region_params.area = nullptr;
753 region_params.region = region;
754 region_params.scene = scene;
755 region_params.notifier = note;
756
757 ED_region_do_listen(&region_params);
758 }
759
760 ED_screen_areas_iter (win, screen, area) {
761 if ((note->category == NC_SPACE) && note->reference) {
762 /* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID
763 * though, the screen, so let notifiers through that reference the entire screen. */
764 if (!ELEM(note->reference, area->spacedata.first, screen, scene)) {
765 continue;
766 }
767 }
768 wmSpaceTypeListenerParams area_params{};
769 area_params.window = win;
770 area_params.area = area;
771 area_params.notifier = note;
772 area_params.scene = scene;
773 ED_area_do_listen(&area_params);
774 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
775 wmRegionListenerParams region_params{};
776 region_params.window = win;
777 region_params.area = area;
778 region_params.region = region;
779 region_params.scene = scene;
780 region_params.notifier = note;
781 ED_region_do_listen(&region_params);
782 }
783 }
784 }
785 }
786
787 MEM_freeN(note);
788 }
789#endif /* If 1 (postpone disabling for in favor of message-bus), eventually. */
790
791 /* Handle message bus. */
792 {
793 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
794 CTX_wm_window_set(C, win);
796 }
797 CTX_wm_window_set(C, nullptr);
798 }
799
801
803
804 /* Status bar. */
805 if (wm->winactive) {
806 wmWindow *win = wm->winactive;
807 CTX_wm_window_set(C, win);
809 CTX_wm_window_set(C, nullptr);
810 }
811
812 /* Auto-run warning. */
814 /* Deprecation warning. */
817
819}
820
821static bool wm_event_always_pass(const wmEvent *event)
822{
823 /* Some events we always pass on, to ensure proper communication. */
824 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
825}
826
838 const wmEvent *event,
839 const eHandlerActionFlag action)
840{
841#ifndef NDEBUG
842 if (C == nullptr || CTX_wm_window(C)) {
844 "Return value for events that should always pass should never be BREAK.");
845 }
846#endif
847 UNUSED_VARS_NDEBUG(C, event, action);
848}
849
851
852/* -------------------------------------------------------------------- */
855
857 wmEventHandler_UI *handler,
858 const wmEvent *event,
859 const bool always_pass)
860{
861 ScrArea *area = CTX_wm_area(C);
862 ARegion *region = CTX_wm_region(C);
863 ARegion *region_popup = CTX_wm_region_popup(C);
864 static bool do_wheel_ui = true;
865 const bool is_wheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN);
866
867 /* UI code doesn't handle return values - it just always returns break.
868 * to make the #DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks. */
869 if (((handler->head.flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) && !ISMOUSE_BUTTON(event->type) &&
870 (event->val == KM_DBL_CLICK))
871 {
872 return WM_HANDLER_CONTINUE;
873 }
874
875 /* UI is quite aggressive with swallowing events, like scroll-wheel. */
876 /* I realize this is not extremely nice code... when UI gets key-maps it can be maybe smarter. */
877 if (do_wheel_ui == false) {
878 if (is_wheel) {
879 return WM_HANDLER_CONTINUE;
880 }
881 if (!wm_event_always_pass(event)) {
882 do_wheel_ui = true;
883 }
884 }
885
886 /* Don't block file-select events. Those are triggered by a separate file browser window.
887 * See #75292. */
888 if (event->type == EVT_FILESELECT) {
889 return WM_HANDLER_CONTINUE;
890 }
891
892 /* We set context to where UI handler came from. */
893 if (handler->context.area) {
894 CTX_wm_area_set(C, handler->context.area);
895 }
896 if (handler->context.region) {
898 }
899 if (handler->context.region_popup) {
902 }
903
904 int retval = handler->handle_fn(C, event, handler->user_data);
905
906 /* Putting back screen context. */
907 if ((retval != WM_UI_HANDLER_BREAK) || always_pass) {
908 CTX_wm_area_set(C, area);
909 CTX_wm_region_set(C, region);
910 BLI_assert((region_popup == nullptr) || screen_temp_region_exists(region_popup));
911 CTX_wm_region_popup_set(C, region_popup);
912 }
913 else {
914 /* This special cases is for areas and regions that get removed. */
915 CTX_wm_area_set(C, nullptr);
916 CTX_wm_region_set(C, nullptr);
917 CTX_wm_region_popup_set(C, nullptr);
918 }
919
920 if (retval == WM_UI_HANDLER_BREAK) {
921 return WM_HANDLER_BREAK;
922 }
923
924 /* Event not handled in UI, if wheel then we temporarily disable it. */
925 if (is_wheel) {
926 do_wheel_ui = false;
927 }
928
929 return WM_HANDLER_CONTINUE;
930}
931
933 wmWindow *win,
934 ARegion *region,
935 bool reactivate_button)
936{
937 if (!region) {
938 return;
939 }
940
941 LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &region->runtime->handlers) {
942 if (handler_base->type == WM_HANDLER_TYPE_UI) {
943 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
944 BLI_assert(handler->handle_fn != nullptr);
945 wmEvent event;
946 wm_event_init_from_window(win, &event);
947 event.type = EVT_BUT_CANCEL;
948 event.val = reactivate_button ? KM_NOTHING : KM_PRESS;
949 event.flag = (eWM_EventFlag)0;
950 handler->handle_fn(C, &event, handler->user_data);
951 }
952 }
953}
954
956{
957 wmWindow *win = CTX_wm_window(C);
958 ARegion *region = CTX_wm_region(C);
959 wm_event_handler_ui_cancel_ex(C, win, region, true);
960}
961
963
964/* -------------------------------------------------------------------- */
969
971{
972 if (win == nullptr) {
973 win = wm->winactive;
974 if (win == nullptr) {
975 win = static_cast<wmWindow *>(wm->windows.first);
976 }
977 }
978
979 ReportList *wm_reports = &wm->runtime->reports;
980
981 /* After adding reports to the global list, reset the report timer. */
982 WM_event_timer_remove(wm, nullptr, wm_reports->reporttimer);
983
984 /* Records time since last report was added. */
985 wm_reports->reporttimer = WM_event_timer_add(wm, win, TIMERREPORT, 0.05);
986
988 wm_reports->reporttimer->customdata = rti;
989}
990
992{
993 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
994 BKE_reports_clear(&wm->runtime->reports);
995 WM_event_timer_remove(wm, nullptr, wm->runtime->reports.reporttimer);
996}
997
998#ifdef WITH_INPUT_NDOF
999void WM_ndof_deadzone_set(float deadzone)
1000{
1001 GHOST_setNDOFDeadZone(deadzone);
1002}
1003#endif
1004
1006{
1007 /* If the caller owns them, handle this. */
1008 if (!reports || BLI_listbase_is_empty(&reports->list) || (reports->flag & RPT_OP_HOLD) != 0) {
1009 return;
1010 }
1011
1012 if (!wm) {
1013 wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
1014 }
1015
1016 /* Add reports to the global list, otherwise they are not seen. */
1018
1019 WM_report_banner_show(wm, nullptr);
1020}
1021
1022void WM_global_report(eReportType type, const char *message)
1023{
1024 /* WARNING: in most cases #BKE_report should be used instead, see doc-string for details. */
1028 BKE_report(&reports, type, message);
1029
1031
1033}
1034
1035void WM_global_reportf(eReportType type, const char *format, ...)
1036{
1037 /* WARNING: in most cases #BKE_reportf should be used instead, see doc-string for details. */
1038
1039 va_list args;
1040
1041 format = RPT_(format);
1042
1043 va_start(args, format);
1044 char *str = BLI_vsprintfN(format, args);
1045 va_end(args);
1046
1047 WM_global_report(type, str);
1048 MEM_freeN(str);
1049}
1050
1052
1053/* -------------------------------------------------------------------- */
1056
1061{
1062 if (wm->undo_stack) {
1063 return intptr_t(wm->undo_stack->step_active);
1064 }
1065 return -1;
1066}
1067
1069{
1070 if (wm->operators.last) {
1071 return intptr_t(wm->operators.last);
1072 }
1073 return -1;
1074}
1075
1077{
1078
1079 LISTBASE_FOREACH (wmOperatorTypeMacro *, otmacro, &ot->macro) {
1080 wmOperatorType *ot_macro = WM_operatortype_find(otmacro->idname, false);
1081
1082 if (!WM_operator_poll(C, ot_macro)) {
1083 return false;
1084 }
1085 }
1086
1087 /* Python needs operator type, so we added exception for it. */
1088 if (ot->pyop_poll) {
1089 return ot->pyop_poll(C, ot);
1090 }
1091 if (ot->poll) {
1092 return ot->poll(C);
1093 }
1094
1095 return true;
1096}
1097
1099{
1100 /* Sets up the new context and calls #wm_operator_invoke() with poll_only. */
1102 C, ot, nullptr, nullptr, static_cast<wmOperatorCallContext>(context), true, nullptr);
1103}
1104
1106{
1107 if (ot->macro.first != nullptr) {
1108 /* For macros, check all have exec() we can call. */
1109 LISTBASE_FOREACH (wmOperatorTypeMacro *, otmacro, &ot->macro) {
1110 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false);
1111 if (otm && WM_operator_ui_poll(otm, ptr)) {
1112 return true;
1113 }
1114 }
1115 return false;
1116 }
1117
1118 if (ot->ui) {
1119 if (ot->ui_poll) {
1120 return ot->ui_poll(ot, ptr);
1121 }
1122 return true;
1123 }
1124
1125 bool result = false;
1126 PointerRNA op_ptr;
1128 RNA_STRUCT_BEGIN (&op_ptr, prop) {
1129 int flag = RNA_property_flag(prop);
1130 if ((flag & PROP_HIDDEN) == 0) {
1131 result = true;
1132 break;
1133 }
1134 }
1136 return result;
1137}
1138
1140{
1141 ScrArea *area = CTX_wm_area(C);
1142 if (area) {
1143 ARegion *region = CTX_wm_region(C);
1144 if (region && region->regiontype == RGN_TYPE_WINDOW) {
1145 area->region_active_win = BLI_findindex(&area->regionbase, region);
1146 }
1147 }
1148}
1149
1154 wmOperator *op,
1155 const wmOperatorStatus retval,
1156 const bool caller_owns_reports)
1157{
1158 if (G.background == 0 && caller_owns_reports == false) { /* Popup. */
1159 if (op->reports->list.first) {
1160 /* FIXME: temp setting window, see other call to #UI_popup_menu_reports for why. */
1161 wmWindow *win_prev = CTX_wm_window(C);
1162 ScrArea *area_prev = CTX_wm_area(C);
1163 ARegion *region_prev = CTX_wm_region(C);
1164
1165 if (win_prev == nullptr) {
1166 CTX_wm_window_set(C, static_cast<wmWindow *>(CTX_wm_manager(C)->windows.first));
1167 }
1168
1170
1171 CTX_wm_window_set(C, win_prev);
1172 CTX_wm_area_set(C, area_prev);
1173 CTX_wm_region_set(C, region_prev);
1174 }
1175 }
1176
1177 if (retval & OPERATOR_FINISHED) {
1178 std::string pystring = WM_operator_pystring(C, op, false, true);
1179 CLOG_STR_INFO(WM_LOG_OPERATORS, 1, pystring.c_str());
1180
1181 if (caller_owns_reports == false) {
1182 /* Print out reports to console.
1183 * When quiet, only show warnings, suppressing info and other non-essential warnings. */
1184 const eReportType level = G.quiet ? RPT_WARNING : RPT_DEBUG;
1185 BKE_reports_print(op->reports, level);
1186 }
1187
1188 if (op->type->flag & OPTYPE_REGISTER) {
1189 if (G.background == 0) { /* Ends up printing these in the terminal, gets annoying. */
1190 /* Report the python string representation of the operator. */
1191 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, pystring.c_str());
1192 }
1193 }
1194 }
1195
1196 /* Refresh Info Editor with reports immediately, even if op returned #OPERATOR_CANCELLED. */
1197 if ((retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) &&
1199 {
1201 }
1202 /* If the caller owns them, handle this. */
1204}
1205
1211{
1212 /* Check undo flag here since undo operators are also added to the list,
1213 * to support checking if the same operator is run twice. */
1214 return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
1215}
1216
1223 wmOperator *op,
1224 const bool repeat,
1225 const bool store,
1226 const bool has_undo_step,
1227 const bool has_register)
1228{
1230 enum {
1231 NOP,
1232 SET,
1233 CLEAR,
1234 } hud_status = NOP;
1235 const bool do_register = (repeat == false) && wm_operator_register_check(wm, op->type);
1236
1237 op->customdata = nullptr;
1238
1239 if (store) {
1241 }
1242
1243 /* We don't want to do undo pushes for operators that are being
1244 * called from operators that already do an undo push. Usually
1245 * this will happen for python operators that call C operators. */
1246 if (wm->op_undo_depth == 0) {
1247 if (op->type->flag & OPTYPE_UNDO) {
1248 ED_undo_push_op(C, op);
1249 if (repeat == 0) {
1250 hud_status = CLEAR;
1251 }
1252 }
1253 else if (op->type->flag & OPTYPE_UNDO_GROUPED) {
1255 if (repeat == 0) {
1256 hud_status = CLEAR;
1257 }
1258 }
1259 else if (has_undo_step) {
1260 /* An undo step was added but the operator wasn't registered (and won't register itself),
1261 * therefor a redo panel wouldn't redo this action but the previous registered action,
1262 * causing the "redo" to remove/loose this operator. See: #101743.
1263 * Register check is needed so nested operator calls don't clear the HUD. See: #103587. */
1264 if (!(has_register || do_register)) {
1265 if (repeat == 0) {
1266 hud_status = CLEAR;
1267 }
1268 }
1269 }
1270 }
1271
1272 if (repeat == 0) {
1273 if (G.debug & G_DEBUG_WM) {
1274 std::string pystring = WM_operator_pystring(C, op, false, true);
1275 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, pystring.c_str());
1276 }
1277
1278 if (do_register) {
1279 /* Take ownership of reports (in case python provided its own). */
1280 op->reports->flag |= RPT_FREE;
1281
1284
1285 if (WM_operator_last_redo(C) == op) {
1286 /* Show the redo panel. */
1287 hud_status = SET;
1288 }
1289 }
1290 else {
1291 WM_operator_free(op);
1292 }
1293 }
1294
1295 if (hud_status != NOP) {
1296 if (hud_status == SET) {
1297 ScrArea *area = CTX_wm_area(C);
1298 if (area && ((area->flag & AREA_FLAG_OFFSCREEN) == 0)) {
1300 }
1301 }
1302 else if (hud_status == CLEAR) {
1303 ED_area_type_hud_clear(wm, nullptr);
1304 }
1305 else {
1307 }
1308 }
1309}
1310
1315 wmOperator *op,
1316 const bool repeat,
1317 const bool store)
1318{
1321
1323
1324 if (op == nullptr || op->type == nullptr) {
1325 return retval;
1326 }
1327
1328 if (0 == WM_operator_poll(C, op->type)) {
1329 return retval;
1330 }
1331
1332 const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
1333 const intptr_t register_id_prev = wm_operator_register_active_id(wm);
1334
1335 if (op->type->exec) {
1336 if (op->type->flag & OPTYPE_UNDO) {
1337 wm->op_undo_depth++;
1338 }
1339
1340 retval = op->type->exec(C, op);
1341 OPERATOR_RETVAL_CHECK(retval);
1342
1343 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1344 wm->op_undo_depth--;
1345 }
1346 }
1347
1348 /* XXX(@mont29): Disabled the repeat check to address part 2 of #31840.
1349 * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
1350 * why this was needed, but worth to note it in case something turns bad. */
1351 if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */) {
1352 wm_operator_reports(C, op, retval, false);
1353 }
1354
1355 if (retval & OPERATOR_FINISHED) {
1356 const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
1357 const bool has_register = (register_id_prev != wm_operator_register_active_id(wm));
1358
1360 C, op, repeat, store && wm->op_undo_depth == 0, has_undo_step, has_register);
1361 }
1362 else if (repeat == 0) {
1363 /* WARNING: modal from exec is bad practice, but avoid crashing. */
1364 if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
1365 WM_operator_free(op);
1366 }
1367 }
1368
1369 return retval | OPERATOR_HANDLED;
1370}
1371
1376{
1378
1379 if (op == nullptr || op->type == nullptr || op->type->exec == nullptr) {
1380 return retval;
1381 }
1382
1383 retval = op->type->exec(C, op);
1384 OPERATOR_RETVAL_CHECK(retval);
1385
1386 return retval;
1387}
1388
1390{
1391 return wm_operator_exec(C, op, false, store);
1392}
1393
1395{
1396 return WM_operator_call_ex(C, op, false);
1397}
1398
1403
1405{
1406 const int op_flag = OP_IS_REPEAT;
1407 op->flag |= op_flag;
1408 const wmOperatorStatus ret = wm_operator_exec(C, op, true, true);
1409 op->flag &= ~op_flag;
1410 return ret;
1411}
1413{
1414 const int op_flag = OP_IS_REPEAT_LAST;
1415 op->flag |= op_flag;
1416 const wmOperatorStatus ret = wm_operator_exec(C, op, true, true);
1417 op->flag &= ~op_flag;
1418 return ret;
1419}
1421{
1422 if (op->type->exec != nullptr) {
1423 return true;
1424 }
1425 if (op->opm) {
1426 /* For macros, check all have exec() we can call. */
1427 LISTBASE_FOREACH (wmOperatorTypeMacro *, otmacro, &op->opm->type->macro) {
1428 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false);
1429 if (otm && otm->exec == nullptr) {
1430 return false;
1431 }
1432 }
1433 return true;
1434 }
1435
1436 return false;
1437}
1438
1440{
1441 /* May be in the operators list or not. */
1442 wmOperator *op_prev;
1443 if (op->prev == nullptr && op->next == nullptr) {
1445 op_prev = static_cast<wmOperator *>(wm->operators.last);
1446 }
1447 else {
1448 op_prev = op->prev;
1449 }
1450 return (op_prev && (op->type == op_prev->type));
1451}
1452
1455 PointerRNA *properties,
1457{
1458 /* Operator-type names are static still (for C++ defined operators).
1459 * Pass to allocation name for debugging. */
1460 wmOperator *op = MEM_callocN<wmOperator>(ot->rna_ext.srna ? __func__ : ot->idname);
1461
1462 /* Adding new operator could be function, only happens here now. */
1463 op->type = ot;
1464 STRNCPY(op->idname, ot->idname);
1465
1466 /* Initialize properties, either copy or create. */
1467 op->ptr = MEM_new<PointerRNA>("wmOperatorPtrRNA");
1468 if (properties && properties->data) {
1469 op->properties = IDP_CopyProperty(static_cast<const IDProperty *>(properties->data));
1470 }
1471 else {
1472 op->properties = blender::bke::idprop::create_group("wmOperatorProperties").release();
1473 }
1474 *op->ptr = RNA_pointer_create_discrete(&wm->id, ot->srna, op->properties);
1475
1476 /* Initialize error reports. */
1477 if (reports) {
1478 op->reports = reports; /* Must be initialized already. */
1479 }
1480 else {
1481 op->reports = MEM_callocN<ReportList>("wmOperatorReportList");
1483 }
1484
1485 /* Recursive filling of operator macro list. */
1486 if (ot->macro.first) {
1487 static wmOperator *motherop = nullptr;
1488 int root = 0;
1489
1490 /* Ensure all ops are in execution order in 1 list. */
1491 if (motherop == nullptr) {
1492 motherop = op;
1493 root = 1;
1494 }
1495
1496 /* If properties exist, it will contain everything needed. */
1497 if (properties) {
1498 wmOperatorTypeMacro *otmacro = static_cast<wmOperatorTypeMacro *>(ot->macro.first);
1499
1500 RNA_STRUCT_BEGIN (properties, prop) {
1501
1502 if (otmacro == nullptr) {
1503 break;
1504 }
1505
1506 /* Skip invalid properties. */
1507 if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
1508 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false);
1509 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
1510 wmOperator *opm = wm_operator_create(wm, otm, &someptr, nullptr);
1511
1513
1514 BLI_addtail(&motherop->macro, opm);
1515 opm->opm = motherop; /* Pointer to mom, for modal(). */
1516
1517 otmacro = otmacro->next;
1518 }
1519 }
1521 }
1522 else {
1523 LISTBASE_FOREACH (wmOperatorTypeMacro *, otmacro, &ot->macro) {
1524 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false);
1525 wmOperator *opm = wm_operator_create(wm, otm, otmacro->ptr, nullptr);
1526
1527 BLI_addtail(&motherop->macro, opm);
1528 opm->opm = motherop; /* Pointer to mom, for modal(). */
1529 }
1530 }
1531
1532 if (root) {
1533 motherop = nullptr;
1534 }
1535 }
1536
1538
1539 return op;
1540}
1541
1547{
1548
1549 bScreen *screen = WM_window_get_active_screen(win);
1550 /* Unlikely but not impossible as this runs after events have been handled. */
1551 if (UNLIKELY(screen == nullptr)) {
1552 return;
1553 }
1554 ED_screen_areas_iter (win, screen, area) {
1555 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
1556 if (region->runtime->gizmo_map != nullptr) {
1557 if (WM_gizmomap_tag_delay_refresh_for_tweak_check(region->runtime->gizmo_map)) {
1558 ED_region_tag_redraw(region);
1559 }
1560 }
1561 }
1562 }
1563}
1564
1566{
1567 ARegion *region = CTX_wm_region(C);
1568 if (region) {
1569 /* Compatibility convention. */
1570 event->mval[0] = event->xy[0] - region->winrct.xmin;
1571 event->mval[1] = event->xy[1] - region->winrct.ymin;
1572 }
1573 else {
1574 /* These values are invalid (avoid odd behavior by relying on old #wmEvent.mval values). */
1575 event->mval[0] = -1;
1576 event->mval[1] = -1;
1577 }
1578}
1579
1585 const wmEvent *event,
1586 PointerRNA *properties,
1588 const bool poll_only,
1589 bool use_last_properties)
1590{
1592
1593 /* This is done because complicated setup is done to call this function
1594 * that is better not duplicated. */
1595 if (poll_only) {
1597 }
1598
1599 if (WM_operator_poll(C, ot)) {
1601 const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
1602 const intptr_t register_id_prev = wm_operator_register_active_id(wm);
1603
1604 /* If `reports == nullptr`, they'll be initialized. */
1605 wmOperator *op = wm_operator_create(wm, ot, properties, reports);
1606
1607 const bool is_nested_call = (wm->op_undo_depth != 0);
1608
1609 if (event != nullptr) {
1610 op->flag |= OP_IS_INVOKE;
1611 }
1612
1613 /* Initialize setting from previous run. */
1614 if (!is_nested_call && use_last_properties) { /* Not called by a Python script. */
1616 }
1617
1618 if ((event == nullptr) || (event->type != MOUSEMOVE)) {
1620 2,
1621 "handle evt %d win %p op %s",
1622 event ? event->type : 0,
1623 CTX_wm_screen(C)->active_region,
1624 ot->idname);
1625 }
1626
1627 if (op->type->invoke && event) {
1628 /* Make a copy of the event as it's `const` and the #wmEvent.mval to be written into. */
1629 wmEvent event_temp = *event;
1630 wm_region_mouse_co(C, &event_temp);
1631
1632 if (op->type->flag & OPTYPE_UNDO) {
1633 wm->op_undo_depth++;
1634 }
1635
1636 retval = op->type->invoke(C, op, &event_temp);
1637 OPERATOR_RETVAL_CHECK(retval);
1638
1639 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1640 wm->op_undo_depth--;
1641 }
1642 }
1643 else if (op->type->exec) {
1644 if (op->type->flag & OPTYPE_UNDO) {
1645 wm->op_undo_depth++;
1646 }
1647
1648 retval = op->type->exec(C, op);
1649 OPERATOR_RETVAL_CHECK(retval);
1650
1651 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
1652 wm->op_undo_depth--;
1653 }
1654 }
1655 else {
1656 /* Debug, important to leave a while, should never happen. */
1657 CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
1658 }
1659
1660 /* NOTE: if the report is given as an argument then assume the caller will deal with displaying
1661 * them currently Python only uses this. */
1662 if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
1663 /* Only show the report if the report list was not given in the function. */
1664 wm_operator_reports(C, op, retval, (reports != nullptr));
1665 }
1666
1667 if (retval & OPERATOR_HANDLED) {
1668 /* Do nothing, #wm_operator_exec() has been called somewhere. */
1669 }
1670 else if (retval & OPERATOR_FINISHED) {
1671 const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
1672 const bool has_register = (register_id_prev != wm_operator_register_active_id(wm));
1673 const bool store = !is_nested_call && use_last_properties;
1674 wm_operator_finished(C, op, false, store, has_undo_step, has_register);
1675 }
1676 else if (retval & OPERATOR_RUNNING_MODAL) {
1677 /* Take ownership of reports (in case python provided its own). */
1678 op->reports->flag |= RPT_FREE;
1679
1680 /* Grab cursor during blocking modal operators (X11)
1681 * Also check for macro. */
1682 if (ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
1684 const rcti *wrap_region = nullptr;
1685
1686 if (event && (U.uiflag & USER_CONTINUOUS_MOUSE)) {
1687 const wmOperator *op_test = op->opm ? op->opm : op;
1688 const wmOperatorType *ot_test = op_test->type;
1689 if ((ot_test->flag & OPTYPE_GRAB_CURSOR_XY) || (op_test->flag & OP_IS_MODAL_GRAB_CURSOR))
1690 {
1692 }
1693 else if (ot_test->flag & OPTYPE_GRAB_CURSOR_X) {
1695 }
1696 else if (ot_test->flag & OPTYPE_GRAB_CURSOR_Y) {
1698 }
1699 }
1700
1701 if (wrap) {
1702 ARegion *region = CTX_wm_region(C);
1703 ScrArea *area = CTX_wm_area(C);
1704
1705 /* Wrap only in X for header. */
1706 if (region && RGN_TYPE_IS_HEADER_ANY(region->regiontype)) {
1708 }
1709
1710 if (region && region->regiontype == RGN_TYPE_WINDOW &&
1711 BLI_rcti_isect_pt_v(&region->winrct, event->xy))
1712 {
1713 wrap_region = &region->winrct;
1714 }
1715 else if (area && BLI_rcti_isect_pt_v(&area->totrct, event->xy)) {
1716 wrap_region = &area->totrct;
1717 }
1718 }
1719
1720 WM_cursor_grab_enable(CTX_wm_window(C), wrap, wrap_region, false);
1721 }
1722
1723 /* Cancel UI handlers, typically tool-tips that can hang around
1724 * while dragging the view or worse, that stay there permanently
1725 * after the modal operator has swallowed all events and passed
1726 * none to the UI handler. */
1728 }
1729 else {
1730 WM_operator_free(op);
1731 }
1732 }
1733
1734 return retval;
1735}
1736
1744 PointerRNA *properties,
1746 const wmOperatorCallContext context,
1747 const bool poll_only,
1748 const wmEvent *event)
1749{
1750 wmOperatorStatus retval;
1751
1753
1754 /* Dummy test. */
1755 if (ot) {
1756 wmWindow *window = CTX_wm_window(C);
1757
1758 if (event == nullptr) {
1759 switch (context) {
1764 case WM_OP_INVOKE_AREA:
1766 /* Window is needed for invoke and cancel operators. */
1767 if (window == nullptr) {
1768 if (poll_only) {
1769 CTX_wm_operator_poll_msg_set(C, "Missing 'window' in context");
1770 }
1771 return wmOperatorStatus(0);
1772 }
1773 else {
1774 event = window->eventstate;
1775 }
1776 break;
1777 default:
1778 event = nullptr;
1779 break;
1780 }
1781 }
1782 else {
1783 switch (context) {
1784 case WM_OP_EXEC_DEFAULT:
1788 case WM_OP_EXEC_AREA:
1789 case WM_OP_EXEC_SCREEN:
1790 event = nullptr;
1791 break;
1792 default:
1793 break;
1794 }
1795 }
1796
1797 switch (context) {
1804 /* Forces operator to go to the region window/channels/preview, for header menus,
1805 * but we stay in the same region if we are already in one. */
1806 ARegion *region = CTX_wm_region(C);
1807 ScrArea *area = CTX_wm_area(C);
1808 int type = RGN_TYPE_WINDOW;
1809
1810 switch (context) {
1813 type = RGN_TYPE_CHANNELS;
1814 break;
1815
1818 type = RGN_TYPE_PREVIEW;
1819 break;
1820
1823 default:
1824 type = RGN_TYPE_WINDOW;
1825 break;
1826 }
1827
1828 if (!(region && region->regiontype == type) && area) {
1829 ARegion *region_other = (type == RGN_TYPE_WINDOW) ?
1831 BKE_area_find_region_type(area, type);
1832 if (region_other) {
1833 CTX_wm_region_set(C, region_other);
1834 }
1835 }
1836
1837 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1838
1839 /* Set region back. */
1840 CTX_wm_region_set(C, region);
1841
1842 return retval;
1843 }
1844 case WM_OP_EXEC_AREA:
1845 case WM_OP_INVOKE_AREA: {
1846 /* Remove region from context. */
1847 ARegion *region = CTX_wm_region(C);
1848
1849 CTX_wm_region_set(C, nullptr);
1850 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1851 CTX_wm_region_set(C, region);
1852
1853 return retval;
1854 }
1855 case WM_OP_EXEC_SCREEN:
1856 case WM_OP_INVOKE_SCREEN: {
1857 /* Remove region + area from context. */
1858 ARegion *region = CTX_wm_region(C);
1859 ScrArea *area = CTX_wm_area(C);
1860
1861 CTX_wm_region_set(C, nullptr);
1862 CTX_wm_area_set(C, nullptr);
1863 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1864 CTX_wm_area_set(C, area);
1865 CTX_wm_region_set(C, region);
1866
1867 return retval;
1868 }
1869 case WM_OP_EXEC_DEFAULT:
1871 return wm_operator_invoke(C, ot, event, properties, reports, poll_only, true);
1872 }
1873 }
1874
1875 return wmOperatorStatus(0);
1876}
1877
1880 wmOperatorCallContext context,
1881 PointerRNA *properties,
1882 const wmEvent *event)
1883{
1884 BLI_assert(ot == WM_operatortype_find(ot->idname, true));
1885 return wm_operator_call_internal(C, ot, properties, nullptr, context, false, event);
1886}
1888 const char *opstring,
1889 wmOperatorCallContext context,
1890 PointerRNA *properties,
1891 const wmEvent *event)
1892{
1893 wmOperatorType *ot = WM_operatortype_find(opstring, false);
1894 if (ot) {
1895 return WM_operator_name_call_ptr(C, ot, context, properties, event);
1896 }
1897
1898 return wmOperatorStatus(0);
1899}
1900
1901bool WM_operator_name_poll(bContext *C, const char *opstring)
1902{
1903 wmOperatorType *ot = WM_operatortype_find(opstring, false);
1904 if (!ot) {
1905 return false;
1906 }
1907
1908 return WM_operator_poll(C, ot);
1909}
1910
1912 const char *opstring,
1913 wmOperatorCallContext context,
1914 IDProperty *properties,
1915 const wmEvent *event)
1916{
1917 wmOperatorType *ot = WM_operatortype_find(opstring, false);
1919 &static_cast<wmWindowManager *>(G_MAIN->wm.first)->id, ot->srna, properties);
1920 return WM_operator_name_call_ptr(C, ot, context, &props_ptr, event);
1921}
1922
1923void WM_menu_name_call(bContext *C, const char *menu_name, short context)
1924{
1925 wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
1928 RNA_string_set(&ptr, "name", menu_name);
1929 WM_operator_name_call_ptr(C, ot, static_cast<wmOperatorCallContext>(context), &ptr, nullptr);
1931}
1932
1935 wmOperatorCallContext context,
1936 PointerRNA *properties,
1938 const bool is_undo)
1939{
1941 /* Not especially nice using undo depth here. It's used so Python never
1942 * triggers undo or stores an operator's last used state. */
1944 if (!is_undo && wm) {
1945 wm->op_undo_depth++;
1946 }
1947
1948 retval = wm_operator_call_internal(C, ot, properties, reports, context, false, nullptr);
1949
1950 if (!is_undo && wm && (wm == CTX_wm_manager(C))) {
1951 wm->op_undo_depth--;
1952 }
1953
1954 return retval;
1955}
1956
1958
1959/* -------------------------------------------------------------------- */
1966
1972
1973static void ui_handler_wait_for_input_remove(bContext *C, void *userdata)
1974{
1975 uiOperatorWaitForInput *opwait = static_cast<uiOperatorWaitForInput *>(userdata);
1976 if (opwait->optype_params.opptr) {
1977 if (opwait->optype_params.opptr->data) {
1978 IDP_FreeProperty(static_cast<IDProperty *>(opwait->optype_params.opptr->data));
1979 }
1980 MEM_delete(opwait->optype_params.opptr);
1981 }
1982
1983 if (opwait->area != nullptr) {
1984 ED_area_status_text(opwait->area, nullptr);
1985 }
1986 else {
1987 ED_workspace_status_text(C, nullptr);
1988 }
1989
1990 MEM_delete(opwait);
1991}
1992
1993static int ui_handler_wait_for_input(bContext *C, const wmEvent *event, void *userdata)
1994{
1995 uiOperatorWaitForInput *opwait = static_cast<uiOperatorWaitForInput *>(userdata);
1996 enum { CONTINUE = 0, EXECUTE, CANCEL } state = CONTINUE;
1997 state = CONTINUE;
1998
1999 switch (event->type) {
2000 case LEFTMOUSE: {
2001 if (event->val == KM_PRESS) {
2002 state = EXECUTE;
2003 }
2004 break;
2005 }
2006 /* Useful if the operator isn't convenient to access while the mouse button is held.
2007 * If it takes numeric input for example. */
2008 case EVT_SPACEKEY:
2009 case EVT_RETKEY: {
2010 if (event->val == KM_PRESS) {
2011 state = EXECUTE;
2012 }
2013 break;
2014 }
2015 case RIGHTMOUSE: {
2016 if (event->val == KM_PRESS) {
2017 state = CANCEL;
2018 }
2019 break;
2020 }
2021 case EVT_ESCKEY: {
2022 if (event->val == KM_PRESS) {
2023 state = CANCEL;
2024 }
2025 break;
2026 }
2027 default: {
2028 break;
2029 }
2030 }
2031
2032 if (state != CONTINUE) {
2033 wmWindow *win = CTX_wm_window(C);
2035
2036 if (state == EXECUTE) {
2037 CTX_store_set(C, opwait->context ? &opwait->context.value() : nullptr);
2039 opwait->optype_params.optype,
2040 opwait->optype_params.opcontext,
2041 opwait->optype_params.opptr,
2042 event);
2043 CTX_store_set(C, nullptr);
2044 }
2045
2049 opwait,
2050 false);
2051
2053
2054 return WM_UI_HANDLER_BREAK;
2055 }
2056
2058}
2059
2062 wmOperatorCallContext opcontext,
2063 PointerRNA *properties,
2064 const wmEvent *event,
2065 const StringRef drawstr)
2066{
2067 bool depends_on_cursor = WM_operator_depends_on_cursor(*C, *ot, properties);
2068
2069 LISTBASE_FOREACH (wmOperatorTypeMacro *, otmacro, &ot->macro) {
2070 if (wmOperatorType *otm = WM_operatortype_find(otmacro->idname, false)) {
2071 if (WM_operator_depends_on_cursor(*C, *otm, properties)) {
2072 depends_on_cursor = true;
2073 }
2074 }
2075 }
2076
2077 if (!depends_on_cursor) {
2078 WM_operator_name_call_ptr(C, ot, opcontext, properties, event);
2079 return;
2080 }
2081
2082 wmWindow *win = CTX_wm_window(C);
2083 /* The operator context is applied when the operator is called,
2084 * the check for the area needs to be explicitly limited here.
2085 * Useful so it's possible to screen-shot an area without drawing into it's header. */
2086 ScrArea *area = WM_OP_CONTEXT_HAS_AREA(opcontext) ? CTX_wm_area(C) : nullptr;
2087
2088 {
2089 std::string header_text = fmt::format(
2090 "{} {}",
2091 IFACE_("Input pending "),
2092 drawstr.is_empty() ? CTX_IFACE_(ot->translation_context, ot->name) : drawstr);
2093 if (area != nullptr) {
2094 ED_area_status_text(area, header_text.c_str());
2095 }
2096 else {
2097 ED_workspace_status_text(C, header_text.c_str());
2098 }
2099 }
2100
2101 WM_cursor_modal_set(win, ot->cursor_pending);
2102
2103 uiOperatorWaitForInput *opwait = MEM_new<uiOperatorWaitForInput>(__func__);
2104 opwait->optype_params.optype = ot;
2105 opwait->optype_params.opcontext = opcontext;
2106 opwait->optype_params.opptr = properties;
2107
2108 opwait->area = area;
2109
2110 if (properties) {
2111 opwait->optype_params.opptr = MEM_new<PointerRNA>(__func__);
2112 *opwait->optype_params.opptr = *properties;
2113 if (properties->data != nullptr) {
2115 static_cast<IDProperty *>(properties->data));
2116 }
2117 }
2118
2119 if (const bContextStore *store = CTX_store_get(C)) {
2120 opwait->context = *store;
2121 }
2122
2124 &win->modalhandlers,
2127 opwait,
2129}
2130
2132
2133/* -------------------------------------------------------------------- */
2138
2140{
2141 /* Future extra custom-data free? */
2142 MEM_freeN(handler);
2143}
2144
2150 wmEventHandler_Op *handler,
2151 const wmEvent *event,
2152 ScrArea **r_area,
2153 ARegion **r_region)
2154{
2155 wmWindow *win = handler->context.win ? handler->context.win : CTX_wm_window(C);
2156 /* It's probably fine to always use #WM_window_get_active_screen() to get the screen. But this
2157 * code has been getting it through context since forever, so play safe and stick to that when
2158 * possible. */
2159 bScreen *screen = handler->context.win ? WM_window_get_active_screen(win) : CTX_wm_screen(C);
2160
2161 *r_area = nullptr;
2162 *r_region = nullptr;
2163
2164 if (screen == nullptr || handler->op == nullptr) {
2165 return;
2166 }
2167
2168 if (handler->context.area == nullptr) {
2169 /* Pass. */
2170 }
2171 else {
2172 ScrArea *area = nullptr;
2173
2174 ED_screen_areas_iter (win, screen, area_iter) {
2175 if (area_iter == handler->context.area) {
2176 area = area_iter;
2177 break;
2178 }
2179 }
2180
2181 if (area == nullptr) {
2182 /* When changing screen layouts with running modal handlers (like render display), this
2183 * is not an error to print. */
2184 if (handler->op == nullptr) {
2186 "internal error: handler (%s) has invalid area",
2187 handler->op->type->idname);
2188 }
2189 }
2190 else {
2191 ARegion *region;
2192 wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : nullptr;
2193 *r_area = area;
2194
2195 if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
2196 region = BKE_area_find_region_xy(area, handler->context.region_type, event->xy);
2197 if (region) {
2198 handler->context.region = region;
2199 }
2200 }
2201 else {
2202 region = nullptr;
2203 }
2204
2205 if ((region == nullptr) && handler->context.region) {
2206 if (BLI_findindex(&area->regionbase, handler->context.region) != -1) {
2207 region = handler->context.region;
2208 }
2209 }
2210
2211 /* No warning print here, after full-area and back regions are remade. */
2212 if (region) {
2213 *r_region = region;
2214 }
2215 }
2216 }
2217}
2218
2219static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
2220{
2221 ScrArea *area = nullptr;
2222 ARegion *region = nullptr;
2223 wm_handler_op_context_get_if_valid(C, handler, event, &area, &region);
2224 CTX_wm_area_set(C, area);
2225 CTX_wm_region_set(C, region);
2226}
2227
2229{
2231
2232 /* C is zero on freeing database, modal handlers then already were freed. */
2233 while (wmEventHandler *handler_base = static_cast<wmEventHandler *>(BLI_pophead(handlers))) {
2234 BLI_assert(handler_base->type != 0);
2235 if (handler_base->type == WM_HANDLER_TYPE_OP) {
2236 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2237
2238 if (handler->op) {
2239 wmWindow *win = CTX_wm_window(C);
2240
2241 if (handler->is_fileselect) {
2242 /* Exit File Browsers referring to this handler/operator. */
2243 LISTBASE_FOREACH (wmWindow *, temp_win, &wm->windows) {
2244 ScrArea *file_area = ED_fileselect_handler_area_find(temp_win, handler->op);
2245 if (!file_area) {
2246 continue;
2247 }
2248 ED_area_exit(C, file_area);
2249 }
2250 }
2251
2252 if (handler->op->type->cancel) {
2253 ScrArea *area = CTX_wm_area(C);
2254 ARegion *region = CTX_wm_region(C);
2255
2256 wm_handler_op_context(C, handler, win->eventstate);
2257
2258 if (handler->op->type->flag & OPTYPE_UNDO) {
2259 wm->op_undo_depth++;
2260 }
2261
2262 handler->op->type->cancel(C, handler->op);
2263
2264 if (handler->op->type->flag & OPTYPE_UNDO) {
2265 wm->op_undo_depth--;
2266 }
2267
2268 CTX_wm_area_set(C, area);
2269 CTX_wm_region_set(C, region);
2270 }
2271
2272 WM_cursor_grab_disable(win, nullptr);
2273
2274 if (handler->is_fileselect) {
2276 }
2277 else {
2278 WM_operator_free(handler->op);
2279 }
2280 }
2281 }
2282 else if (handler_base->type == WM_HANDLER_TYPE_UI) {
2283 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
2284
2285 if (handler->remove_fn) {
2286 ScrArea *area_prev = CTX_wm_area(C);
2287 ARegion *region_prev = CTX_wm_region(C);
2288 ARegion *region_popup_prev = CTX_wm_region_popup(C);
2289
2290 if (handler->context.area) {
2291 CTX_wm_area_set(C, handler->context.area);
2292 }
2293 if (handler->context.region) {
2294 CTX_wm_region_set(C, handler->context.region);
2295 }
2296 if (handler->context.region_popup) {
2299 }
2300
2301 handler->remove_fn(C, handler->user_data);
2302
2303 /* Currently we don't have a practical way to check if this region
2304 * was a temporary region created by `handler`, so do a full lookup. */
2305 if (region_popup_prev && !screen_temp_region_exists(region_popup_prev)) {
2306 region_popup_prev = nullptr;
2307 }
2308
2309 CTX_wm_area_set(C, area_prev);
2310 CTX_wm_region_set(C, region_prev);
2311 CTX_wm_region_popup_set(C, region_popup_prev);
2312 }
2313 }
2314
2315 wm_event_free_handler(handler_base);
2316 }
2317}
2318
2319BLI_INLINE bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
2320{
2321 if (kmi->flag & KMI_INACTIVE) {
2322 return false;
2323 }
2324
2325 if (winevent->flag & WM_EVENT_IS_REPEAT) {
2326 if (kmi->flag & KMI_REPEAT_IGNORE) {
2327 return false;
2328 }
2329 }
2330
2331 const int kmitype = WM_userdef_event_map(kmi->type);
2332
2333 /* The matching rules. */
2334 if (kmitype == KM_TEXTINPUT) {
2335 if (winevent->val == KM_PRESS) { /* Prevent double clicks. */
2336 if (ISKEYBOARD(winevent->type) && winevent->utf8_buf[0]) {
2337 return true;
2338 }
2339 }
2340 }
2341
2342 if (kmitype != KM_ANY) {
2343 if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
2344 const wmTabletData *wmtab = &winevent->tablet;
2345
2346 if (winevent->type != LEFTMOUSE) {
2347 /* Tablet events can occur on hover + key-press. */
2348 return false;
2349 }
2350 if ((kmitype == TABLET_STYLUS) && (wmtab->active != EVT_TABLET_STYLUS)) {
2351 return false;
2352 }
2353 if ((kmitype == TABLET_ERASER) && (wmtab->active != EVT_TABLET_ERASER)) {
2354 return false;
2355 }
2356 }
2357 else {
2358 if (winevent->type != kmitype) {
2359 return false;
2360 }
2361 }
2362 }
2363
2364 if (kmi->val != KM_ANY) {
2365 if (winevent->val != kmi->val) {
2366 return false;
2367 }
2368 }
2369
2370 if (kmi->val == KM_CLICK_DRAG) {
2371 if (kmi->direction != KM_ANY) {
2372 if (kmi->direction != winevent->direction) {
2373 return false;
2374 }
2375 }
2376 }
2377
2378 /* Account for rare case of when these keys are used as the 'type' not as modifiers. */
2379 if (kmi->shift != KM_ANY) {
2380 const int8_t shift = (winevent->modifier & KM_SHIFT) ? KM_MOD_HELD : KM_NOTHING;
2381 if ((shift != kmi->shift) && !ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) {
2382 return false;
2383 }
2384 }
2385 if (kmi->ctrl != KM_ANY) {
2386 const int8_t ctrl = (winevent->modifier & KM_CTRL) ? KM_MOD_HELD : KM_NOTHING;
2387 if ((ctrl != kmi->ctrl) && !ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) {
2388 return false;
2389 }
2390 }
2391 if (kmi->alt != KM_ANY) {
2392 const int8_t alt = (winevent->modifier & KM_ALT) ? KM_MOD_HELD : KM_NOTHING;
2393 if ((alt != kmi->alt) && !ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) {
2394 return false;
2395 }
2396 }
2397 if (kmi->oskey != KM_ANY) {
2398 const int8_t oskey = (winevent->modifier & KM_OSKEY) ? KM_MOD_HELD : KM_NOTHING;
2399 if ((oskey != kmi->oskey) && (winevent->type != EVT_OSKEY)) {
2400 return false;
2401 }
2402 }
2403 if (kmi->hyper != KM_ANY) {
2404 const int8_t hyper = (winevent->modifier & KM_HYPER) ? KM_MOD_HELD : KM_NOTHING;
2405 if ((hyper != kmi->hyper) && (winevent->type != EVT_HYPER)) {
2406 return false;
2407 }
2408 }
2409
2410 /* Only key-map entry with key-modifier is checked,
2411 * means all keys without modifier get handled too. */
2412 /* That is currently needed to make overlapping events work (when you press A - G fast or so). */
2413 if (kmi->keymodifier) {
2414 if (winevent->keymodifier != kmi->keymodifier) {
2415 return false;
2416 }
2417 }
2418
2419 return true;
2420}
2421
2423 wmOperator *op,
2424 const wmEvent *event)
2425{
2426 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
2427 /* Should already be handled by #wm_user_modal_keymap_set_items. */
2428 BLI_assert(kmi->propvalue_str[0] == '\0');
2429 if (wm_eventmatch(event, kmi)) {
2430 if ((keymap->poll_modal_item == nullptr) || keymap->poll_modal_item(op, kmi->propvalue)) {
2431 return kmi;
2432 }
2433 }
2434 }
2435 return nullptr;
2436}
2437
2444
2456 wmOperator *op,
2457 wmEvent *event,
2458 wmEvent_ModalMapStore *event_backup)
2459{
2460 BLI_assert(event->type != EVT_MODAL_MAP);
2461
2462 /* Support for modal key-map in macros. */
2463 if (op->opm) {
2464 op = op->opm;
2465 }
2466
2467 event_backup->dbl_click_disabled = false;
2468
2469 if (op->type->modalkeymap) {
2471 wmKeyMapItem *kmi = nullptr;
2472
2473 const wmEvent *event_match = nullptr;
2474 wmEvent event_no_dbl_click;
2475
2476 if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, event))) {
2477 event_match = event;
2478 }
2479 else if (event->val == KM_DBL_CLICK) {
2480 event_no_dbl_click = *event;
2481 event_no_dbl_click.val = KM_PRESS;
2482 if ((kmi = wm_eventmatch_modal_keymap_items(keymap, op, &event_no_dbl_click))) {
2483 event_match = &event_no_dbl_click;
2484 }
2485 }
2486
2487 if (event_match != nullptr) {
2488 event_backup->prev_type = event->prev_type;
2489 event_backup->prev_val = event->prev_val;
2490
2491 event->prev_type = event_match->type;
2492 event->prev_val = event_match->val;
2493 event->type = EVT_MODAL_MAP;
2494 event->val = kmi->propvalue;
2495
2496 /* Avoid double-click events even in the case of #EVT_MODAL_MAP,
2497 * since it's possible users configure double-click key-map items
2498 * which would break when modal functions expect press/release. */
2499 if (event->prev_val == KM_DBL_CLICK) {
2500 event->prev_val = KM_PRESS;
2501 event_backup->dbl_click_disabled = true;
2502 }
2503 }
2504 }
2505
2506 if (event->type != EVT_MODAL_MAP) {
2507 /* This bypass just disables support for double-click in modal handlers. */
2508 if (event->val == KM_DBL_CLICK) {
2509 event->val = KM_PRESS;
2510 event_backup->dbl_click_disabled = true;
2511 }
2512 }
2513}
2514
2522static void wm_event_modalkeymap_end(wmEvent *event, const wmEvent_ModalMapStore *event_backup)
2523{
2524 if (event->type == EVT_MODAL_MAP) {
2525 event->type = event->prev_type;
2526 event->val = event->prev_val;
2527
2528 event->prev_type = event_backup->prev_type;
2529 event->prev_val = event_backup->prev_val;
2530 }
2531
2532 if (event_backup->dbl_click_disabled) {
2533 event->val = KM_DBL_CLICK;
2534 }
2535}
2536
2541{
2542 if (!(handler->op->type->flag & OPTYPE_MODAL_PRIORITY)) {
2543 /* Keep priority operators in front. */
2544 wmEventHandler *last_priority_handler = nullptr;
2545 LISTBASE_FOREACH (wmEventHandler *, handler_iter, &win->modalhandlers) {
2546 if (handler_iter->type == WM_HANDLER_TYPE_OP) {
2547 wmEventHandler_Op *handler_iter_op = (wmEventHandler_Op *)handler_iter;
2548 if (handler_iter_op->op != nullptr) {
2549 if (handler_iter_op->op->type->flag & OPTYPE_MODAL_PRIORITY) {
2550 last_priority_handler = handler_iter;
2551 }
2552 }
2553 }
2554 }
2555
2556 if (last_priority_handler) {
2557 BLI_insertlinkafter(&win->modalhandlers, last_priority_handler, handler);
2558 return;
2559 }
2560 }
2561
2562 BLI_addhead(&win->modalhandlers, handler);
2563}
2564
2569 ListBase *handlers,
2570 wmEventHandler *handler_base,
2571 wmEvent *event,
2572 PointerRNA *properties,
2573 const char *kmi_idname)
2574{
2576
2577 /* Derived, modal or blocking operator. */
2578 if ((handler_base->type == WM_HANDLER_TYPE_OP) &&
2579 (((wmEventHandler_Op *)handler_base)->op != nullptr))
2580 {
2581 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
2582 wmOperator *op = handler->op;
2583 wmOperatorType *ot = op->type;
2584
2586 /* Interface is locked and operator is not allowed to run,
2587 * nothing to do in this case. */
2588 }
2589 else if (ot->modal) {
2590 /* We set context to where modal handler came from. */
2592 wmWindow *win = CTX_wm_window(C);
2593 ScrArea *area = CTX_wm_area(C);
2594 ARegion *region = CTX_wm_region(C);
2595
2596 wm_handler_op_context(C, handler, event);
2597 wm_region_mouse_co(C, event);
2598
2599 wmEvent_ModalMapStore event_backup;
2600 wm_event_modalkeymap_begin(C, op, event, &event_backup);
2601
2602 const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
2603 const intptr_t register_id_prev = wm_operator_register_active_id(wm);
2604 if (ot->flag & OPTYPE_UNDO) {
2605 wm->op_undo_depth++;
2606 }
2607
2608 /* Warning, after this call all context data and 'event' may be freed. see check below. */
2609 retval = ot->modal(C, op, event);
2610 OPERATOR_RETVAL_CHECK(retval);
2611
2612 if (ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
2613 wm->op_undo_depth--;
2614 }
2615
2616 /* When the window changes the modal modifier may have loaded a new blend file
2617 * (the `system_demo_mode` add-on does this), so we have to assume the event,
2618 * operator, area, region etc have all been freed. */
2619 if (CTX_wm_window(C) == win) {
2620
2621 wm_event_modalkeymap_end(event, &event_backup);
2622
2623 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2624 wm_operator_reports(C, op, retval, false);
2625
2626 wmOperator *op_test = handler->op->opm ? handler->op->opm : handler->op;
2627 if (op_test->type->modalkeymap) {
2629 }
2630 }
2631 else {
2632 /* Not very common, but modal operators may report before finishing. */
2633 if (!BLI_listbase_is_empty(&op->reports->list)) {
2636 }
2637 }
2638
2639 /* Important to run 'wm_operator_finished' before setting the context members to null. */
2640 if (retval & OPERATOR_FINISHED) {
2641 const bool has_undo_step = (undo_id_prev != wm_operator_undo_active_id(wm));
2642 const bool has_register = (register_id_prev != wm_operator_register_active_id(wm));
2643
2644 wm_operator_finished(C, op, false, true, has_undo_step, has_register);
2645 handler->op = nullptr;
2646 }
2647 else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2648 WM_operator_free(op);
2649 handler->op = nullptr;
2650 }
2651
2652 /* Putting back screen context, `reval` can pass through after modal failures! */
2653 if ((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
2654 CTX_wm_area_set(C, area);
2655 CTX_wm_region_set(C, region);
2656 }
2657 else {
2658 /* This special cases is for areas and regions that get removed. */
2659 CTX_wm_area_set(C, nullptr);
2660 CTX_wm_region_set(C, nullptr);
2661 }
2662
2663 /* Update gizmos during modal handlers. */
2664 wm_gizmomaps_handled_modal_update(C, event, handler);
2665
2666 /* Remove modal handler, operator itself should have been canceled and freed. */
2667 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2669
2670 BLI_remlink(handlers, handler);
2671 wm_event_free_handler(&handler->head);
2672
2673 /* Prevent silly errors from operator users. */
2674 // retval &= ~OPERATOR_PASS_THROUGH;
2675 }
2676 }
2677 }
2678 else {
2679 CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
2680 }
2681 }
2682 else {
2683 wmOperatorType *ot = WM_operatortype_find(kmi_idname, false);
2684
2686 bool use_last_properties = true;
2687 PointerRNA tool_properties = {};
2688
2689 bToolRef *keymap_tool = nullptr;
2690 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
2691 keymap_tool = ((wmEventHandler_Keymap *)handler_base)->keymap_tool;
2692 }
2693 else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
2694 wmGizmoMap *gizmo_map = ((wmEventHandler_Gizmo *)handler_base)->gizmo_map;
2695 wmGizmo *gz = wm_gizmomap_highlight_get(gizmo_map);
2696 if (gz && (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT)) {
2697 keymap_tool = WM_toolsystem_ref_from_context(C);
2698 }
2699 }
2700
2701 const bool is_tool = (keymap_tool != nullptr);
2702 const bool use_tool_properties = is_tool;
2703
2704 if (use_tool_properties) {
2706 keymap_tool, &tool_properties, properties, ot);
2707 properties = &tool_properties;
2708 use_last_properties = false;
2709 }
2710
2711 retval = wm_operator_invoke(C, ot, event, properties, nullptr, false, use_last_properties);
2712
2713 if (use_tool_properties) {
2714 WM_operator_properties_free(&tool_properties);
2715 }
2716
2717 /* Link gizmo if #WM_GIZMOGROUPTYPE_TOOL_INIT is set. */
2718 if (retval & OPERATOR_FINISHED) {
2719 if (is_tool) {
2720 bToolRef_Runtime *tref_rt = keymap_tool->runtime;
2721 if (tref_rt->gizmo_group[0]) {
2722 const char *idname = tref_rt->gizmo_group;
2723 wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(idname, false);
2724 if (gzgt != nullptr) {
2725 if ((gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_INIT) != 0) {
2726 ARegion *region = CTX_wm_region(C);
2727 if (region != nullptr) {
2729 WM_gizmo_group_type_ensure_ptr_ex(gzgt, gzmap_type);
2731 gzmap_type, gzgt, region);
2732 /* We can't rely on drawing to initialize gizmo's since disabling
2733 * overlays/gizmos will prevent pre-drawing setup calls, see #60905. */
2734 WM_gizmogroup_ensure_init(C, gzgroup);
2735 }
2736 }
2737 }
2738 }
2739 }
2740 }
2741 /* Done linking gizmo. */
2742 }
2743 }
2744
2745 /* Finished and pass through flag as handled. */
2746 if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH)) {
2747 return WM_HANDLER_HANDLED;
2748 }
2749
2750 /* Modal unhandled, break. */
2753 }
2754
2755 if (retval & OPERATOR_PASS_THROUGH) {
2756 return WM_HANDLER_CONTINUE;
2757 }
2758
2759 return WM_HANDLER_BREAK;
2760}
2761
2763{
2764 LISTBASE_FOREACH (bScreen *, screen, &G_MAIN->screens) {
2765 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
2766 if (area->spacetype == SPACE_FILE) {
2767 SpaceFile *sfile = static_cast<SpaceFile *>(area->spacedata.first);
2768 if (sfile->op == file_operator) {
2769 sfile->op = nullptr;
2770 }
2771 }
2772 }
2773 }
2774
2775 WM_operator_free(file_operator);
2776}
2777
2783 ListBase *handlers,
2784 wmEventHandler_Op *handler,
2785 int val)
2786{
2789
2790 switch (val) {
2792 wmWindow *win = CTX_wm_window(C);
2793 const blender::int2 window_size = WM_window_native_pixel_size(win);
2794 const blender::int2 window_center = window_size / 2;
2795
2796 const rcti window_rect = {
2797 /*xmin*/ window_center[0],
2798 /*xmax*/ window_center[0] + int(U.file_space_data.temp_win_sizex * UI_SCALE_FAC),
2799 /*ymin*/ window_center[1],
2800 /*ymax*/ window_center[1] + int(U.file_space_data.temp_win_sizey * UI_SCALE_FAC),
2801 };
2802
2804 IFACE_("Blender File View"),
2805 &window_rect,
2806 SPACE_FILE,
2807 U.filebrowser_display_type,
2808 true))
2809 {
2810 ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
2811
2812 BLI_assert(area->spacetype == SPACE_FILE);
2813
2814 region_header->flag |= RGN_FLAG_HIDDEN;
2815 /* Header on bottom, #AZone triangle to toggle header looks misplaced at the top. */
2816 region_header->alignment = RGN_ALIGN_BOTTOM;
2817
2818 /* Settings for file-browser, #sfile is not operator owner but sends events. */
2819 SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
2820 sfile->op = handler->op;
2821
2823 }
2824 else {
2825 BKE_report(&wm->runtime->reports, RPT_ERROR, "Failed to open window!");
2826 return WM_HANDLER_BREAK;
2827 }
2828
2829 action = WM_HANDLER_BREAK;
2830 break;
2831 }
2832
2836 wmWindow *ctx_win = CTX_wm_window(C);
2837 wmEvent *eventstate = ctx_win->eventstate;
2838 /* The root window of the operation as determined in #WM_event_add_fileselect(). */
2839 wmWindow *root_win = handler->context.win;
2840
2841 /* Remove link now, for load file case before removing. */
2842 BLI_remlink(handlers, handler);
2843
2844 if (val == EVT_FILESELECT_EXTERNAL_CANCEL) {
2845 /* The window might have been freed already. */
2846 if (BLI_findindex(&wm->windows, handler->context.win) == -1) {
2847 handler->context.win = nullptr;
2848 }
2849 }
2850 else {
2851 ScrArea *ctx_area = CTX_wm_area(C);
2852
2853 wmWindow *temp_win = nullptr;
2854 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
2855 bScreen *screen = WM_window_get_active_screen(win);
2856 ScrArea *file_area = static_cast<ScrArea *>(screen->areabase.first);
2857
2858 if ((file_area->spacetype != SPACE_FILE) || !WM_window_is_temp_screen(win)) {
2859 continue;
2860 }
2861
2862 if (file_area->full) {
2863 /* Users should not be able to maximize/full-screen an area in a temporary screen.
2864 * So if there's a maximized file browser in a temporary screen,
2865 * it was likely opened by #EVT_FILESELECT_FULL_OPEN. */
2866 continue;
2867 }
2868
2869 int win_size[2];
2870 bool is_maximized;
2871 ED_fileselect_window_params_get(win, win_size, &is_maximized);
2873 static_cast<SpaceFile *>(file_area->spacedata.first), win_size, is_maximized);
2874
2875 if (BLI_listbase_is_single(&file_area->spacedata)) {
2876 BLI_assert(root_win != win);
2877
2878 wm_window_close(C, wm, win);
2879
2880 /* #wm_window_close() sets the context's window to null. */
2881 CTX_wm_window_set(C, root_win);
2882
2883 /* Some operators expect a drawable context (for #EVT_FILESELECT_EXEC). */
2884 wm_window_make_drawable(wm, root_win);
2885 /* Ensure correct cursor position, otherwise, popups may close immediately after
2886 * opening (#UI_BLOCK_MOVEMOUSE_QUIT). */
2887 int xy[2];
2888 if (wm_cursor_position_get(root_win, &xy[0], &xy[1])) {
2889 copy_v2_v2_int(eventstate->xy, xy);
2890 }
2891 wm->winactive = root_win; /* Reports use this... */
2892 }
2893 else if (file_area->full) {
2894 ED_screen_full_prevspace(C, file_area);
2895 }
2896 else {
2897 ED_area_prevspace(C, file_area);
2898 }
2899
2900 temp_win = win;
2901 break;
2902 }
2903
2904 if (!temp_win && ctx_area->full) {
2906 static_cast<SpaceFile *>(ctx_area->spacedata.first), nullptr, false);
2907 ED_screen_full_prevspace(C, ctx_area);
2908 }
2909 }
2910
2911 CTX_wm_window_set(C, root_win);
2912 wm_handler_op_context(C, handler, eventstate);
2913 /* At this point context is supposed to match the root context determined by
2914 * #WM_event_add_fileselect(). */
2915 BLI_assert(!CTX_wm_area(C) || (CTX_wm_area(C) == handler->context.area));
2916 BLI_assert(!CTX_wm_region(C) || (CTX_wm_region(C) == handler->context.region));
2917
2918 ScrArea *handler_area = CTX_wm_area(C);
2919 /* Make sure new context area is ready, the operator callback may operate on it. */
2920 if (handler_area) {
2921 ED_area_do_refresh(C, handler_area);
2922 }
2923
2924 /* Needed for #UI_popup_menu_reports. */
2925
2926 if (val == EVT_FILESELECT_EXEC) {
2927 if (handler->op->type->flag & OPTYPE_UNDO) {
2928 wm->op_undo_depth++;
2929 }
2930
2931 const wmOperatorStatus retval = handler->op->type->exec(C, handler->op);
2932 OPERATOR_RETVAL_CHECK(retval);
2933
2934 /* XXX check this carefully, `CTX_wm_manager(C) == wm` is a bit hackish. */
2935 if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
2936 wm->op_undo_depth--;
2937 }
2938
2939 /* XXX check this carefully, `CTX_wm_manager(C) == wm` is a bit hackish. */
2940 if (retval & OPERATOR_FINISHED) {
2941 if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
2942 if (handler->op->type->flag & OPTYPE_UNDO) {
2943 ED_undo_push_op(C, handler->op);
2944 }
2945 else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED) {
2946 ED_undo_grouped_push_op(C, handler->op);
2947 }
2948 }
2949 }
2950
2951 if (handler->op->reports->list.first) {
2952
2953 /* FIXME(@ideasman42): temp setting window, this is really bad!
2954 * only have because lib linking errors need to be seen by users :(
2955 * it can be removed without breaking anything but then no linking errors. */
2956 wmWindow *win_prev = CTX_wm_window(C);
2957 ScrArea *area_prev = CTX_wm_area(C);
2958 ARegion *region_prev = CTX_wm_region(C);
2959
2960 if (win_prev == nullptr) {
2961 CTX_wm_window_set(C, static_cast<wmWindow *>(CTX_wm_manager(C)->windows.first));
2962 }
2963
2965 UI_popup_menu_reports(C, handler->op->reports);
2966
2968
2969 CTX_wm_window_set(C, win_prev);
2970 CTX_wm_area_set(C, area_prev);
2971 CTX_wm_region_set(C, region_prev);
2972 }
2973
2974 /* For #WM_operator_pystring only, custom report handling is done above. */
2975 wm_operator_reports(C, handler->op, retval, true);
2976
2977 if (retval & OPERATOR_FINISHED) {
2979 }
2980
2981 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2983 }
2984 }
2985 else {
2986 if (handler->op->type->cancel) {
2987 if (handler->op->type->flag & OPTYPE_UNDO) {
2988 wm->op_undo_depth++;
2989 }
2990
2991 handler->op->type->cancel(C, handler->op);
2992
2993 if (handler->op->type->flag & OPTYPE_UNDO) {
2994 wm->op_undo_depth--;
2995 }
2996 }
2998 }
2999
3000 CTX_wm_area_set(C, nullptr);
3001
3002 wm_event_free_handler(&handler->head);
3003
3004 action = WM_HANDLER_BREAK;
3005 break;
3006 }
3007 }
3008
3009 return action;
3010}
3011
3013 ListBase *handlers,
3014 wmEventHandler_Op *handler,
3015 const wmEvent *event)
3016{
3018
3019 if (event->type != EVT_FILESELECT) {
3020 return action;
3021 }
3022 if (handler->op != (wmOperator *)event->customdata) {
3023 return action;
3024 }
3025
3026 return wm_handler_fileselect_do(C, handlers, handler, event->val);
3027}
3028
3030{
3031 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
3032}
3033
3035{
3036 if (action & WM_HANDLER_BREAK) {
3037 return "handled";
3038 }
3039 if (action & WM_HANDLER_HANDLED) {
3040 return "handled (and pass on)";
3041 }
3042 return "un-handled";
3043}
3044
3045static std::optional<std::string> keymap_handler_log_kmi_event_str(const wmKeyMapItem *kmi)
3046{
3047 /* Short representation of the key that was pressed,
3048 * include this since it may differ from the event in minor details
3049 * which can help looking up the key-map definition. */
3050 return WM_keymap_item_to_string(kmi, false);
3051}
3052
3054{
3055 /* The key-map item properties can further help distinguish this item from others. */
3056 std::optional<std::string> kmi_props;
3057 if (kmi->properties != nullptr) {
3059 if (ot) {
3060 kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512);
3061 }
3062 else { /* Fallback. */
3063 char *c_str = IDP_reprN(kmi->properties, nullptr);
3064 kmi_props = c_str;
3065 MEM_freeN(c_str);
3066 }
3067 }
3068 return fmt::format("{}({})", kmi->idname, kmi_props.value_or(""));
3069}
3070
3071#define PRINT \
3072 if (do_debug_handler) \
3073 printf
3074
3076 /* From 'wm_handlers_do_intern'. */
3077 bContext *C,
3078 wmEvent *event,
3079 ListBase *handlers,
3080 wmEventHandler_Keymap *handler,
3081 /* Additional. */
3082 wmKeyMap *keymap,
3083 const bool do_debug_handler)
3084{
3086
3087 if (keymap == nullptr) {
3088 /* Only callback is allowed to have nullptr key-maps. */
3089 BLI_assert(handler->dynamic.keymap_fn);
3090 }
3091 else {
3092 PRINT("%s: checking '%s' ...", __func__, keymap->idname);
3093
3094 if (WM_keymap_poll(C, keymap)) {
3095
3096 PRINT("pass\n");
3097
3098 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
3099 if (wm_eventmatch(event, kmi)) {
3100 wmEventHandler_KeymapPost keymap_post = handler->post;
3101
3102 action |= wm_handler_operator_call(
3103 C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
3104
3106 2,
3107 "keymap '%s', %s, %s, event: %s",
3108 keymap->idname,
3109 keymap_handler_log_kmi_op_str(C, kmi).c_str(),
3111 keymap_handler_log_kmi_event_str(kmi).value_or("").c_str());
3112
3113 if (action & WM_HANDLER_BREAK) {
3114 /* Not always_pass here, it denotes removed handler_base. */
3115 if (keymap_post.post_fn != nullptr) {
3116 keymap_post.post_fn(keymap, kmi, keymap_post.user_data);
3117 }
3118 break;
3119 }
3120 }
3121 }
3122 }
3123 else {
3124 PRINT("fail\n");
3125 }
3126 }
3127
3128 return action;
3129}
3130
3132 /* From #wm_handlers_do_intern. */
3133 bContext *C,
3134 wmEvent *event,
3135 ListBase *handlers,
3136 wmEventHandler_Gizmo *handler,
3137 /* Additional. */
3138 wmGizmoGroup *gzgroup,
3139 wmKeyMap *keymap,
3140 const bool do_debug_handler,
3141 bool *r_keymap_poll)
3142{
3144 bool keymap_poll = false;
3145
3146 PRINT("%s: checking '%s' ...", __func__, keymap->idname);
3147
3148 if (WM_keymap_poll(C, keymap)) {
3149 keymap_poll = true;
3150 PRINT("pass\n");
3151 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
3152 if (wm_eventmatch(event, kmi)) {
3153 PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
3154
3155 CTX_wm_gizmo_group_set(C, gzgroup);
3156
3157 /* `handler->op` is called later, we want key-map op to be triggered here. */
3158 action |= wm_handler_operator_call(
3159 C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
3160
3161 CTX_wm_gizmo_group_set(C, nullptr);
3162
3163 if (action & WM_HANDLER_BREAK) {
3164 if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
3165 printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname);
3166 }
3167 break;
3168 }
3169 if (action & WM_HANDLER_HANDLED) {
3170 if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
3171 printf("%s: handled - and pass on! '%s'\n", __func__, kmi->idname);
3172 }
3173 }
3174 else {
3175 PRINT("%s: un-handled '%s'\n", __func__, kmi->idname);
3176 }
3177 }
3178 }
3179 }
3180 else {
3181 PRINT("fail\n");
3182 }
3183
3184 if (r_keymap_poll) {
3185 *r_keymap_poll = keymap_poll;
3186 }
3187
3188 return action;
3189}
3190
3192 wmWindowManager *wm,
3193 wmEventHandler_Gizmo *handler,
3194 wmEvent *event,
3195 const bool always_pass,
3196 ListBase *handlers,
3197 const bool do_debug_handler)
3198{
3200
3201 /* NOTE(@ideasman42): early exit for always-pass events (typically timers)
3202 * which pass through from running modal operators which may have started them.
3203 * In the case of blocking modal operators, it's not expected that gizmos would
3204 * be used at the same time as navigating or painting for example.
3205 *
3206 * In principle these could be handled, however in practice:
3207 * `handle_highlight` & `handle_keymap` would be set to false for timers,
3208 * making this function do practically nothing.
3209 *
3210 * Early exit to avoid complicating checks below.
3211 * The early return can be replaced with checks that only run
3212 * necessary logic if these events need to be handled in the future.
3213 *
3214 * Without this, gizmos can become highlighted and the cursor changed
3215 * while navigating in the 3D viewport, see: #139681. */
3216 if (always_pass) {
3217 return action;
3218 }
3219
3220 /* Drag events use the previous click location to highlight the gizmos,
3221 * Get the highlight again in case the user dragged off the gizmo. */
3222 const bool is_event_drag = (event->val == KM_CLICK_DRAG);
3223 const bool is_event_modifier = ISKEYMODIFIER(event->type);
3224 /* Only keep the highlight if the gizmo becomes modal as result of event handling.
3225 * Without this check, even un-handled drag events will set the highlight if the drag
3226 * was initiated over a gizmo. */
3227 const bool restore_highlight_unless_activated = is_event_drag;
3228
3229 ScrArea *area = CTX_wm_area(C);
3230 ARegion *region = CTX_wm_region(C);
3231 wmGizmoMap *gzmap = handler->gizmo_map;
3232 BLI_assert(gzmap != nullptr);
3234
3235 if (gz && ISMOUSE(event->type) && event->val == KM_PRESS) {
3236 /* Remove any tooltips on mouse down. #83589 */
3238 }
3239
3240 /* Needed so UI blocks over gizmos don't let events fall through to the gizmos,
3241 * noticeable for the node editor - where dragging on a node should move it, see: #73212.
3242 * note we still allow for starting the gizmo drag outside, then travel 'inside' the node. */
3243 if (region->runtime->type->clip_gizmo_events_by_ui) {
3244 if (UI_region_block_find_mouse_over(region, event->xy, true)) {
3245 if (gz != nullptr && event->type != EVT_GIZMO_UPDATE) {
3246 if (restore_highlight_unless_activated == false) {
3248 wm_gizmomap_highlight_set(gzmap, C, nullptr, 0);
3249 }
3250 }
3251 return action;
3252 }
3253 }
3254
3255 struct PrevGizmoData {
3256 wmGizmo *gz_modal;
3257 wmGizmo *gz;
3258 int part;
3259 };
3260 PrevGizmoData prev{};
3261 prev.gz_modal = wm_gizmomap_modal_get(gzmap);
3262 prev.gz = gz;
3263 prev.part = gz ? gz->highlight_part : 0;
3264
3265 if (region->runtime->gizmo_map != handler->gizmo_map) {
3267 }
3268
3270 wm_region_mouse_co(C, event);
3271
3272 bool handle_highlight = false;
3273 bool handle_keymap = false;
3274
3275 /* Handle gizmo highlighting. */
3276 if ((prev.gz_modal == nullptr) &&
3277 ((event->type == MOUSEMOVE) || is_event_modifier || is_event_drag))
3278 {
3279 handle_highlight = true;
3280 if (is_event_modifier || is_event_drag) {
3281 handle_keymap = true;
3282 }
3283 }
3284 else {
3285 handle_keymap = true;
3286 }
3287
3288 /* There is no need to handle this event when the key-map isn't being applied
3289 * since any change to the highlight will be restored to the previous value. */
3290 if (restore_highlight_unless_activated) {
3291 if ((handle_highlight == true) && (handle_keymap == false)) {
3292 return action;
3293 }
3294 }
3295
3296 if (prev.gz_modal == nullptr) {
3297 if (handle_highlight == false && wm_gizmomap_highlight_pending(gzmap)) {
3298 handle_highlight = true;
3299 }
3300 }
3301
3302 if (handle_highlight) {
3303 int part = -1;
3304 gz = wm_gizmomap_highlight_find(gzmap, C, event, &part);
3305
3306 /* If no gizmos are/were active, don't clear tool-tips. */
3307 if (gz || prev.gz) {
3308 if ((prev.gz != gz) || (prev.part != part)) {
3310 }
3311 }
3312
3313 if (wm_gizmomap_highlight_set(gzmap, C, gz, part)) {
3314 if (gz != nullptr) {
3315 if ((U.flag & USER_TOOLTIPS) && (gz->flag & WM_GIZMO_NO_TOOLTIP) == 0) {
3317 }
3318 }
3319 }
3320
3322 }
3323
3324 /* Don't use from now on. */
3325 bool is_event_handle_all = gz && (gz->flag & WM_GIZMO_EVENT_HANDLE_ALL);
3326
3327 if (handle_keymap) {
3328 /* Handle highlight gizmo. */
3329 if ((gz != nullptr) && (gz->flag & WM_GIZMO_HIDDEN_KEYMAP) == 0) {
3330 bool keymap_poll = false;
3331 wmGizmoGroup *gzgroup = gz->parent_gzgroup;
3332 wmKeyMap *keymap = WM_keymap_active(wm, gz->keymap ? gz->keymap : gzgroup->type->keymap);
3334 C, event, handlers, handler, gzgroup, keymap, do_debug_handler, &keymap_poll);
3335
3336#ifdef USE_GIZMO_MOUSE_PRIORITY_HACK
3337 if (((action & WM_HANDLER_BREAK) == 0) && !is_event_handle_all && keymap_poll) {
3338 if ((event->val == KM_PRESS) && ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
3339
3340 wmEvent event_test_click = *event;
3341 event_test_click.val = KM_CLICK;
3342
3343 wmEvent event_test_click_drag = *event;
3344 event_test_click_drag.val = KM_CLICK_DRAG;
3345
3346 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
3347 if ((kmi->flag & KMI_INACTIVE) == 0) {
3348 if (wm_eventmatch(&event_test_click, kmi) ||
3349 wm_eventmatch(&event_test_click_drag, kmi))
3350 {
3351 wmOperatorType *ot = WM_operatortype_find(kmi->idname, false);
3353 is_event_handle_all = true;
3354 break;
3355 }
3356 }
3357 }
3358 }
3359 }
3360 }
3361#endif /* `USE_GIZMO_MOUSE_PRIORITY_HACK` */
3362 }
3363
3364 /* Don't use from now on. */
3365 gz = nullptr;
3366
3367 /* Fall back to selected gizmo (when un-handled). */
3368 if ((action & WM_HANDLER_BREAK) == 0) {
3369 if (WM_gizmomap_is_any_selected(gzmap)) {
3370 const ListBase *groups = WM_gizmomap_group_list(gzmap);
3371 LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, groups) {
3372 if (wm_gizmogroup_is_any_selected(gzgroup)) {
3373 wmKeyMap *keymap = WM_keymap_active(wm, gzgroup->type->keymap);
3375 C, event, handlers, handler, gzgroup, keymap, do_debug_handler, nullptr);
3376 if (action & WM_HANDLER_BREAK) {
3377 break;
3378 }
3379 }
3380 }
3381 }
3382 }
3383 }
3384
3385 if (handle_highlight) {
3386 if (restore_highlight_unless_activated) {
3387 /* Check handling the key-map didn't activate a gizmo. */
3388 wmGizmo *gz_modal = wm_gizmomap_modal_get(gzmap);
3389 if (!(gz_modal && (gz_modal != prev.gz_modal))) {
3390 wm_gizmomap_highlight_set(gzmap, C, prev.gz, prev.part);
3391 }
3392 }
3393 }
3394
3395 if (is_event_handle_all) {
3396 if (action == WM_HANDLER_CONTINUE) {
3398 }
3399 }
3400
3401 /* Restore the area. */
3402 CTX_wm_area_set(C, area);
3403 CTX_wm_region_set(C, region);
3404
3405 return action;
3406}
3407
3409
3410/* -------------------------------------------------------------------- */
3413
3415 wmWindow *win,
3416 wmEvent *event,
3417 ListBase *handlers)
3418{
3419 const bool do_debug_handler =
3420 (G.debug & G_DEBUG_HANDLERS) &&
3421 /* Comment this out to flood the console! (if you really want to test). */
3422 !ISMOUSE_MOTION(event->type);
3423
3426
3427 if (handlers == nullptr) {
3428 wm_event_handler_return_value_check(C, event, action);
3429 return action;
3430 }
3431
3432 /* Modal handlers can get removed in this loop, we keep the loop this way.
3433 *
3434 * NOTE: check 'handlers->first' because in rare cases the handlers can be cleared
3435 * by the event that's called, for eg:
3436 *
3437 * Calling a python script which changes the area.type, see #32232. */
3438 for (wmEventHandler *handler_base = static_cast<wmEventHandler *>(handlers->first),
3439 *handler_base_next;
3440 handler_base && handlers->first;
3441 handler_base = handler_base_next)
3442 {
3443 handler_base_next = handler_base->next;
3444
3445 /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
3446 if (handler_base->flag & WM_HANDLER_DO_FREE) {
3447 /* Pass. */
3448 }
3449 else if (handler_base->poll == nullptr ||
3450 handler_base->poll(win, CTX_wm_area(C), CTX_wm_region(C), event))
3451 {
3452 /* In advance to avoid access to freed event on window close. */
3453 const bool always_pass = wm_event_always_pass(event);
3454
3455 /* Modal+blocking handler_base. */
3456 if (handler_base->flag & WM_HANDLER_BLOCKING) {
3457 action |= WM_HANDLER_BREAK;
3458 }
3459
3460 /* Handle all types here. */
3461 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
3462 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
3464 WM_event_get_keymaps_from_handler(wm, win, handler, &km_result);
3466 /* Compute in advance, as event may be freed on WM_HANDLER_BREAK. */
3467 const bool event_is_timer = ISTIMER(event->type);
3468 for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) {
3469 wmKeyMap *keymap = km_result.keymaps[km_index];
3471 C, event, handlers, handler, keymap, do_debug_handler);
3472 if (action_iter & WM_HANDLER_BREAK) {
3473 break;
3474 }
3475 }
3476 action |= action_iter;
3477
3478 /* Clear the tool-tip whenever a key binding is handled, without this tool-tips
3479 * are kept when a modal operators starts (annoying but otherwise harmless). */
3480 if (action & WM_HANDLER_BREAK && !event_is_timer) {
3481 /* Window may be gone after file read. */
3482 if (CTX_wm_window(C) != nullptr) {
3484 }
3485 }
3486 }
3487 else if (handler_base->type == WM_HANDLER_TYPE_UI) {
3488 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
3489 BLI_assert(handler->handle_fn != nullptr);
3490 if (!wm->runtime->is_interface_locked) {
3491 action |= wm_handler_ui_call(C, handler, event, always_pass);
3492 }
3493 }
3494 else if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
3495 wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
3496 if (!wm->runtime->is_interface_locked && event->type == EVT_DROP) {
3497 LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
3498 /* Other drop custom types allowed. */
3499 if (event->custom == EVT_DATA_DRAGDROP) {
3500 ListBase *lb = (ListBase *)event->customdata;
3501 LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) {
3502 if (drop->poll(C, drag, event)) {
3503 wm_drop_prepare(C, drag, drop);
3504
3505 /* Pass single matched #wmDrag onto the operator. */
3506 BLI_remlink(lb, drag);
3507 ListBase single_lb = {nullptr};
3508 BLI_addtail(&single_lb, drag);
3509 event->customdata = &single_lb;
3510
3512 const wmOperatorStatus op_retval =
3513 drop->ot ? wm_operator_call_internal(
3514 C, drop->ot, drop->ptr, nullptr, opcontext, false, event) :
3516 OPERATOR_RETVAL_CHECK(op_retval);
3517
3518 if ((op_retval & OPERATOR_CANCELLED) && drop->cancel) {
3519 drop->cancel(CTX_data_main(C), drag, drop);
3520 }
3521
3522 action |= WM_HANDLER_BREAK;
3523
3524 /* Free the drags. */
3526 WM_drag_free_list(&single_lb);
3527
3528 wm_event_custom_clear(event);
3529
3530 wm_drop_end(C, drag, drop);
3531
3532 /* XXX file-read case. */
3533 if (CTX_wm_window(C) == nullptr) {
3534 return action;
3535 }
3536
3537 /* Escape from drag loop, got freed. */
3538 break;
3539 }
3540 }
3541 /* Always exit all drags on a drop event, even if poll didn't succeed. */
3542 wm_drags_exit(wm, win);
3543 }
3544 }
3545 }
3546 }
3547 else if (handler_base->type == WM_HANDLER_TYPE_GIZMO) {
3548 wmEventHandler_Gizmo *handler = (wmEventHandler_Gizmo *)handler_base;
3550 C, wm, handler, event, always_pass, handlers, do_debug_handler);
3551 }
3552 else if (handler_base->type == WM_HANDLER_TYPE_OP) {
3553 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
3554 if (handler->is_fileselect) {
3555 if (!wm->runtime->is_interface_locked) {
3556 /* Screen context changes here. */
3557 action |= wm_handler_fileselect_call(C, handlers, handler, event);
3558 }
3559 }
3560 else {
3561 action |= wm_handler_operator_call(C, handlers, handler_base, event, nullptr, nullptr);
3562 }
3563 }
3564 else {
3565 /* Unreachable (handle all types above). */
3567 }
3568
3569 if (action & WM_HANDLER_BREAK) {
3570 if (always_pass) {
3571 action &= ~WM_HANDLER_BREAK;
3572 }
3573 else {
3574 break;
3575 }
3576 }
3577 }
3578
3579 /* File-read case, if the wm is freed then the handler's
3580 * will have been too so the code below need not run. */
3581 if (CTX_wm_window(C) == nullptr) {
3582 return action;
3583 }
3584
3585 /* Code this for all modal ops, and ensure free only happens here. */
3586
3587 /* The handler Could be freed already by regular modal ops. */
3588 if (BLI_findindex(handlers, handler_base) != -1) {
3589 /* Modal UI handler can be tagged to be freed. */
3590 if (handler_base->flag & WM_HANDLER_DO_FREE) {
3591 BLI_remlink(handlers, handler_base);
3592 wm_event_free_handler(handler_base);
3593 }
3594 }
3595 }
3596
3597 if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL)) {
3599 }
3600
3601 /* Do some extra sanity checking before returning the action. */
3602 wm_event_handler_return_value_check(C, event, action);
3603 return action;
3604}
3605
3606#undef PRINT
3607
3608/* This calls handlers twice - to solve (double-)click events. */
3610{
3611 eHandlerActionFlag action = wm_handlers_do_intern(C, CTX_wm_window(C), event, handlers);
3612
3613 /* Will be nullptr in the file read case. */
3614 wmWindow *win = CTX_wm_window(C);
3615 if (win == nullptr) {
3616 return action;
3617 }
3618
3619 if (ISMOUSE_MOTION(event->type)) {
3620 /* Test for #KM_CLICK_DRAG events. */
3621
3622 /* NOTE(@ideasman42): Needed so drag can be used for editors that support both click
3623 * selection and passing through the drag action to box select. See #WM_generic_select_modal.
3624 * Unlike click, accept `action` when break isn't set.
3625 * Operators can return `OPERATOR_FINISHED | OPERATOR_PASS_THROUGH` which results
3626 * in `action` setting #WM_HANDLER_HANDLED, but not #WM_HANDLER_BREAK. */
3627 if ((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action)) {
3628 if (win->event_queue_check_drag) {
3629 if ((event->flag & WM_EVENT_FORCE_DRAG_THRESHOLD) ||
3630 WM_event_drag_test(event, event->prev_press_xy))
3631 {
3633 const int direction = WM_event_drag_direction(event);
3634
3635 /* Intentionally leave `event->xy` as-is, event users are expected to use
3636 * `event->prev_press_xy` if they need to access the drag start location. */
3637 const short prev_val = event->val;
3638 const wmEventType prev_type = event->type;
3639 const wmEventModifierFlag prev_modifier = event->modifier;
3640 const wmEventType prev_keymodifier = event->keymodifier;
3641
3642 event->val = KM_CLICK_DRAG;
3643 event->type = event->prev_press_type;
3644 event->modifier = event->prev_press_modifier;
3645 event->keymodifier = event->prev_press_keymodifier;
3646 event->direction = direction;
3647
3648 CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK_DRAG");
3649
3650 action |= wm_handlers_do_intern(C, win, event, handlers);
3651
3652 event->direction = 0;
3653 event->keymodifier = prev_keymodifier;
3654 event->modifier = prev_modifier;
3655 event->val = prev_val;
3656 event->type = prev_type;
3657
3658 win->event_queue_check_click = false;
3659 if (!((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action))) {
3660 /* Only disable when handled as other handlers may use this drag event. */
3661 CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: drag was generated & handled");
3662 win->event_queue_check_drag = false;
3663 }
3664 }
3665 }
3666 }
3667 else {
3668 if (win->event_queue_check_drag) {
3669 CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: motion event was handled");
3670 win->event_queue_check_drag = false;
3671 }
3672 }
3673 }
3674 else if (ISKEYBOARD_OR_BUTTON(event->type)) {
3675 /* All events that don't set #wmEvent.prev_type must be ignored. */
3676
3677 /* Test for CLICK events. */
3678 if (wm_action_not_handled(action)) {
3679 /* #wmWindow.eventstate stores if previous event was a #KM_PRESS, in case that
3680 * wasn't handled, the #KM_RELEASE will become a #KM_CLICK. */
3681
3682 if (event->val == KM_PRESS) {
3683 if ((event->flag & WM_EVENT_IS_REPEAT) == 0) {
3684 win->event_queue_check_click = true;
3685
3686 CLOG_INFO(WM_LOG_HANDLERS, 3, "detecting CLICK_DRAG: press event detected");
3687 win->event_queue_check_drag = true;
3688
3689 win->event_queue_check_drag_handled = false;
3690 }
3691 }
3692 else if (event->val == KM_RELEASE) {
3693 if (win->event_queue_check_drag) {
3694 if ((event->prev_press_type != event->type) &&
3695 (ISKEYMODIFIER(event->type) || (event->type == event->prev_press_keymodifier)))
3696 {
3697 /* Support releasing modifier keys without canceling the drag event, see #89989. */
3698 }
3699 else {
3700 CLOG_INFO(
3701 WM_LOG_HANDLERS, 3, "CLICK_DRAG: canceling (release event didn't match press)");
3702 win->event_queue_check_drag = false;
3703 }
3704 }
3705 }
3706
3707 if (event->val == KM_RELEASE) {
3708 if (event->prev_press_type == event->type) {
3709 if (event->prev_val == KM_PRESS) {
3710 if (win->event_queue_check_click) {
3711 if (WM_event_drag_test(event, event->prev_press_xy)) {
3712 win->event_queue_check_click = false;
3713 if (win->event_queue_check_drag) {
3715 3,
3716 "CLICK_DRAG: canceling (key-release exceeds drag threshold)");
3717 win->event_queue_check_drag = false;
3718 }
3719 }
3720 else {
3721 /* Position is where the actual click happens, for more
3722 * accurate selecting in case the mouse drifts a little. */
3723 const int xy[2] = {UNPACK2(event->xy)};
3724
3725 copy_v2_v2_int(event->xy, event->prev_press_xy);
3726 event->val = KM_CLICK;
3727
3728 CLOG_INFO(WM_LOG_HANDLERS, 1, "CLICK: handling");
3729
3730 action |= wm_handlers_do_intern(C, win, event, handlers);
3731
3732 event->val = KM_RELEASE;
3733 copy_v2_v2_int(event->xy, xy);
3734 }
3735 }
3736 }
3737 }
3738 }
3739 else if (event->val == KM_DBL_CLICK) {
3740 /* The underlying event is a press, so try and handle this. */
3741 event->val = KM_PRESS;
3742 action |= wm_handlers_do_intern(C, win, event, handlers);
3743
3744 /* Revert value if not handled. */
3745 if (wm_action_not_handled(action)) {
3746 event->val = KM_DBL_CLICK;
3747 }
3748 }
3749 }
3750 else {
3751 win->event_queue_check_click = false;
3752
3753 if (win->event_queue_check_drag) {
3755 3,
3756 "CLICK_DRAG: canceling (button event was handled: value=%d)",
3757 event->val);
3758 win->event_queue_check_drag = false;
3759 }
3760 }
3761 }
3762 else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) {
3763 /* Modifiers which can trigger click event's,
3764 * however we don't want this if the mouse wheel has been used, see #74607. */
3765 if (wm_action_not_handled(action)) {
3766 /* Pass. */
3767 }
3768 else {
3769 if (ISKEYMODIFIER(event->prev_type)) {
3770 win->event_queue_check_click = false;
3771 }
3772 }
3773 }
3774
3775 wm_event_handler_return_value_check(C, event, action);
3776 return action;
3777}
3778
3780
3781/* -------------------------------------------------------------------- */
3786
3787static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect)
3788{
3789 if (wm_event_always_pass(event)) {
3790 return true;
3791 }
3792 if (BLI_rcti_isect_pt_v(rect, event->xy)) {
3793 return true;
3794 }
3795 return false;
3796}
3797
3798static ScrArea *area_event_inside(bContext *C, const int xy[2])
3799{
3800 wmWindow *win = CTX_wm_window(C);
3801 bScreen *screen = CTX_wm_screen(C);
3802
3803 if (screen) {
3804 ED_screen_areas_iter (win, screen, area) {
3805 if (BLI_rcti_isect_pt_v(&area->totrct, xy)) {
3806 return area;
3807 }
3808 }
3809 }
3810 return nullptr;
3811}
3812
3813static ARegion *region_event_inside(bContext *C, const int xy[2])
3814{
3815 bScreen *screen = CTX_wm_screen(C);
3816 ScrArea *area = CTX_wm_area(C);
3817
3818 if (screen && area) {
3819 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
3820 if (BLI_rcti_isect_pt_v(&region->winrct, xy)) {
3821 return region;
3822 }
3823 }
3824 }
3825 return nullptr;
3826}
3827
3829{
3830 if (region) {
3832 if (pc->poll == nullptr || pc->poll(C)) {
3833 wmWindow *win = CTX_wm_window(C);
3834 WM_paint_cursor_tag_redraw(win, region);
3835 }
3836 }
3837 }
3838}
3839
3845static void wm_paintcursor_test(bContext *C, const wmEvent *event)
3846{
3848
3849 if (wm->paintcursors.first) {
3850 const bScreen *screen = CTX_wm_screen(C);
3851 ARegion *region = screen ? screen->active_region : nullptr;
3852
3853 if (region) {
3854 ARegion *prev_region = CTX_wm_region(C);
3855
3856 CTX_wm_region_set(C, region);
3857 wm_paintcursor_tag(C, wm, region);
3858 CTX_wm_region_set(C, prev_region);
3859 }
3860
3861 /* If previous position was not in current region, we have to set a temp new context. */
3862 if (region == nullptr || !BLI_rcti_isect_pt_v(&region->winrct, event->prev_xy)) {
3863 ScrArea *prev_area = CTX_wm_area(C);
3864 ARegion *prev_region = CTX_wm_region(C);
3865
3868
3870
3871 CTX_wm_area_set(C, prev_area);
3872 CTX_wm_region_set(C, prev_region);
3873 }
3874 }
3875}
3876
3878 wmWindow *win,
3879 wmEvent *event)
3880{
3881 bScreen *screen = WM_window_get_active_screen(win);
3882
3883 if (BLI_listbase_is_empty(&wm->drags)) {
3884 return WM_HANDLER_CONTINUE;
3885 }
3886
3887 if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
3888 screen->do_draw_drag = true;
3889 }
3890 else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
3891 wm_drags_exit(wm, win);
3893
3894 screen->do_draw_drag = true;
3895
3896 return WM_HANDLER_BREAK;
3897 }
3898 else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
3899 event->type = EVT_DROP;
3900
3901 /* Create custom-data, first free existing. */
3902 wm_event_custom_free(event);
3903 wm_event_custom_clear(event);
3904
3905 event->custom = EVT_DATA_DRAGDROP;
3906 event->customdata = &wm->drags;
3907 event->customdata_free = true;
3908
3909 /* Clear drop icon. */
3910 screen->do_draw_drag = true;
3911
3912 /* Restore cursor (disabled, see `wm_dragdrop.cc`). */
3913 // WM_cursor_modal_restore(win);
3914 }
3915
3916 return WM_HANDLER_CONTINUE;
3917}
3918
3922static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
3923{
3924 if (win->pie_event_type_lock && win->pie_event_type_lock == event->type) {
3925 if (event->val == KM_RELEASE) {
3927 return false;
3928 }
3929 return true;
3930 }
3931 return false;
3932}
3933
3942{
3944 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
3945 if (BLI_remlink_safe(&win->runtime->event_queue, event)) {
3946 wm_event_free(event);
3947 return;
3948 }
3949 }
3950 }
3951}
3952
3954
3955/* -------------------------------------------------------------------- */
3960
3961#ifdef WITH_XR_OPENXR
3968static void wm_event_handle_xrevent(bContext *C,
3969 wmWindowManager *wm,
3970 wmWindow *win,
3971 wmEvent *event)
3972{
3973 ScrArea *area = WM_xr_session_area_get(&wm->xr);
3974 if (!area) {
3975 return;
3976 }
3977 BLI_assert(area->spacetype == SPACE_VIEW3D && area->spacedata.first);
3978
3979 /* Find a valid region for XR operator execution and modal handling. */
3981 if (!region) {
3982 return;
3983 }
3984 BLI_assert(WM_region_use_viewport(area, region)); /* For operators using GPU-based selection. */
3985
3986 CTX_wm_area_set(C, area);
3987 CTX_wm_region_set(C, region);
3988
3989 eHandlerActionFlag action = wm_handlers_do(C, event, &win->modalhandlers);
3990
3991 if ((action & WM_HANDLER_BREAK) == 0) {
3992 wmXrActionData *actiondata = static_cast<wmXrActionData *>(event->customdata);
3993 if (actiondata->ot->modal && event->val == KM_RELEASE) {
3994 /* Don't execute modal operators on release. */
3995 }
3996 else {
3997 PointerRNA properties{};
3998 properties.type = actiondata->ot->srna;
3999 properties.data = actiondata->op_properties;
4000 if (actiondata->ot->invoke) {
4001 /* Invoke operator, either executing operator or transferring responsibility to window
4002 * modal handlers. */
4004 actiondata->ot,
4005 event,
4006 actiondata->op_properties ? &properties : nullptr,
4007 nullptr,
4008 false,
4009 false);
4010 }
4011 else {
4012 /* Execute operator. */
4014 wm, actiondata->ot, actiondata->op_properties ? &properties : nullptr, nullptr);
4015 if ((WM_operator_call(C, op) & OPERATOR_HANDLED) == 0) {
4016 WM_operator_free(op);
4017 }
4018 }
4019 }
4020 }
4021
4022 CTX_wm_region_set(C, nullptr);
4023 CTX_wm_area_set(C, nullptr);
4024}
4025#endif /* WITH_XR_OPENXR */
4026
4028{
4029 if (region->runtime->type->do_lock) {
4030 /* If the region is locked, we ignore the events. Handling them can trigger depsgraph
4031 * evaluations in some cases which is not safe to do because another thread may evaluate the
4032 * depsgraph already. */
4033 if (wm_event_always_pass(event)) {
4034 return WM_HANDLER_CONTINUE;
4035 }
4036 return WM_HANDLER_BREAK;
4037 }
4038
4039 CTX_wm_region_set(C, region);
4040
4041 /* Call even on non mouse events, since the. */
4042 wm_region_mouse_co(C, event);
4043
4044 const wmWindowManager *wm = CTX_wm_manager(C);
4045 if (!BLI_listbase_is_empty(&wm->drags)) {
4046 /* Does polls for drop regions and checks #uiButs. */
4047 /* Need to be here to make sure region context is true. */
4048 if (ELEM(event->type, MOUSEMOVE, EVT_DROP) || ISKEYMODIFIER(event->type)) {
4049 wm_drags_check_ops(C, event);
4050 }
4051 }
4052
4053 return wm_handlers_do(C, event, &region->runtime->handlers);
4054}
4055
4064 wmEvent *event,
4065 ScrArea *area)
4066{
4067 /* Case 1. */
4068 if (wm_event_always_pass(event)) {
4070
4071 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
4072 action |= wm_event_do_region_handlers(C, event, region);
4073 }
4074
4075 wm_event_handler_return_value_check(C, event, action);
4076 return action;
4077 }
4078
4079 /* Case 2. */
4080 ARegion *region_hovered = ED_area_find_region_xy_visual(area, RGN_TYPE_ANY, event->xy);
4081 if (!region_hovered) {
4082 return WM_HANDLER_CONTINUE;
4083 }
4084
4085 return wm_event_do_region_handlers(C, event, region_hovered);
4086}
4087
4089{
4092
4093 /* Begin GPU render boundary - Certain event handlers require GPU usage. */
4095
4096 /* Update key configuration before handling events. */
4099
4100 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
4101 bScreen *screen = WM_window_get_active_screen(win);
4102
4103 /* Some safety checks - these should always be set! */
4107
4108 if (screen == nullptr) {
4109 wm_event_free_all(win);
4110 }
4111
4112 wmEvent *event;
4113 while ((event = static_cast<wmEvent *>(win->runtime->event_queue.first))) {
4115
4116 /* Force handling drag if a key is pressed even if the drag threshold has not been met.
4117 * Needed so tablet actions (which typically use a larger threshold) can click-drag
4118 * then press keys - activating the drag action early.
4119 * Limit to mouse-buttons drag actions interrupted by pressing any non-mouse button.
4120 * Otherwise pressing two keys on the keyboard will interpret this as a drag action. */
4121 if (win->event_queue_check_drag) {
4122 if ((event->val == KM_PRESS) && ((event->flag & WM_EVENT_IS_REPEAT) == 0) &&
4124 {
4125 event = wm_event_add_mousemove_to_head(win);
4126 event->flag |= WM_EVENT_FORCE_DRAG_THRESHOLD;
4127 }
4128 }
4129 const bool event_queue_check_drag_prev = win->event_queue_check_drag;
4130
4131 {
4132 const bool is_consecutive = WM_event_consecutive_gesture_test(event);
4134 if (event->type == win->event_queue_consecutive_gesture_type) {
4135 event->flag |= WM_EVENT_IS_CONSECUTIVE;
4136 }
4137 else if (is_consecutive || WM_event_consecutive_gesture_test_break(win, event)) {
4138 CLOG_INFO(WM_LOG_HANDLERS, 1, "consecutive gesture break (%d)", event->type);
4141 }
4142 }
4143 else if (is_consecutive) {
4144 CLOG_INFO(WM_LOG_HANDLERS, 1, "consecutive gesture begin (%d)", event->type);
4145 win->event_queue_consecutive_gesture_type = event->type;
4147 /* While this should not be set, it's harmless to free here. */
4149 }
4150 }
4151
4152 /* Active screen might change during handlers, update pointer. */
4153 screen = WM_window_get_active_screen(win);
4154
4155 if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ISMOUSE_MOTION(event->type)) {
4156 printf("\n%s: Handling event\n", __func__);
4157 WM_event_print(event);
4158 }
4159
4160 /* Take care of pie event filter. */
4161 if (wm_event_pie_filter(win, event)) {
4162 if (!ISMOUSE_MOTION(event->type)) {
4163 CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
4164 }
4165 BLI_remlink(&win->runtime->event_queue, event);
4166 wm_event_free_last_handled(win, event);
4167 continue;
4168 }
4169
4170 CTX_wm_window_set(C, win);
4171
4172#ifdef WITH_XR_OPENXR
4173 if (event->type == EVT_XR_ACTION) {
4174 wm_event_handle_xrevent(C, wm, win, event);
4175 BLI_remlink(&win->runtime->event_queue, event);
4176 wm_event_free_last_handled(win, event);
4177 /* Skip mouse event handling below, which is unnecessary for XR events. */
4178 continue;
4179 }
4180#endif
4181
4182 /* Clear tool-tip on mouse move. */
4183 if (screen->tool_tip && screen->tool_tip->exit_on_event) {
4184 if (ISMOUSE_MOTION(event->type)) {
4185 if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) >
4187 {
4188 WM_tooltip_clear(C, win);
4189 }
4190 }
4191 }
4192
4193 /* We let modal handlers get active area/region, also wm_paintcursor_test needs it. */
4196
4197 /* MVC demands to not draw in event handlers...
4198 * but we need to leave it for GPU selecting etc. */
4199 wm_window_make_drawable(wm, win);
4200
4201 wm_region_mouse_co(C, event);
4202
4203 /* First we do priority handlers, modal + some limited key-maps. */
4204 action |= wm_handlers_do(C, event, &win->modalhandlers);
4205
4206 /* File-read case. */
4207 if (CTX_wm_window(C) == nullptr) {
4210 return;
4211 }
4212
4213 /* Check for a tool-tip. */
4214 if (screen == WM_window_get_active_screen(win)) {
4215 if (screen->tool_tip && screen->tool_tip->timer) {
4216 if ((event->type == TIMER) && (event->customdata == screen->tool_tip->timer)) {
4217 WM_tooltip_init(C, win);
4218 }
4219 }
4220 }
4221
4222 /* Check dragging, creates new event or frees, adds draw tag. */
4223 action |= wm_event_drag_and_drop_test(wm, win, event);
4224
4225 if ((action & WM_HANDLER_BREAK) == 0) {
4226 /* NOTE: setting sub-window active should be done here,
4227 * after modal handlers have been done. */
4228 if (event->type == MOUSEMOVE) {
4229 /* State variables in screen, cursors.
4230 * Also used in `wm_draw.cc`, fails for modal handlers though. */
4231 ED_screen_set_active_region(C, win, event->xy);
4232 /* For regions having custom cursors. */
4233 wm_paintcursor_test(C, event);
4234 }
4235#ifdef WITH_INPUT_NDOF
4236 else if (event->type == NDOF_MOTION) {
4237 win->addmousemove = true;
4238 }
4239#endif
4240
4241 ED_screen_areas_iter (win, screen, area) {
4242 /* After restoring a screen from SCREENMAXIMIZED we have to wait
4243 * with the screen handling till the region coordinates are updated. */
4244 if (screen->skip_handling) {
4245 /* Restore for the next iteration of wm_event_do_handlers. */
4246 screen->skip_handling = false;
4247 break;
4248 }
4249
4250 /* Update action-zones if needed,
4251 * done here because it needs to be independent from redraws. */
4252 if (area->flag & AREA_FLAG_ACTIONZONES_UPDATE) {
4253 ED_area_azones_update(area, event->xy);
4254 }
4255
4256 if (wm_event_inside_rect(event, &area->totrct)) {
4257 CTX_wm_area_set(C, area);
4258
4259 action |= wm_event_do_handlers_area_regions(C, event, area);
4260
4261 /* File-read case (Python), #29489. */
4262 if (CTX_wm_window(C) == nullptr) {
4265 return;
4266 }
4267
4268 CTX_wm_region_set(C, nullptr);
4269
4270 if ((action & WM_HANDLER_BREAK) == 0) {
4271 wm_region_mouse_co(C, event); /* Only invalidates `event->mval` in this case. */
4272 action |= wm_handlers_do(C, event, &area->handlers);
4273 }
4274 CTX_wm_area_set(C, nullptr);
4275
4276 /* NOTE: do not escape on #WM_HANDLER_BREAK,
4277 * mouse-move needs handled for previous area. */
4278 }
4279 }
4280
4281 if ((action & WM_HANDLER_BREAK) == 0) {
4282 /* Also some non-modal handlers need active area/region. */
4285
4286 wm_region_mouse_co(C, event);
4287
4288 action |= wm_handlers_do(C, event, &win->handlers);
4289
4290 /* File-read case. */
4291 if (CTX_wm_window(C) == nullptr) {
4294 return;
4295 }
4296 }
4297 }
4298
4299 /* If press was handled, we don't want to do click. This way
4300 * press in tool key-map can override click in editor key-map. */
4301 if (ISMOUSE_BUTTON(event->type) && event->val == KM_PRESS && !wm_action_not_handled(action))
4302 {
4303 win->event_queue_check_click = false;
4304 }
4305
4306 /* If the drag even was handled, don't attempt to keep re-handing the same
4307 * drag event on every cursor motion, see: #87511. */
4309 win->event_queue_check_drag = false;
4310 win->event_queue_check_drag_handled = false;
4311 }
4312
4313 if (event_queue_check_drag_prev && (win->event_queue_check_drag == false)) {
4315 }
4316
4317 /* Update previous mouse position for following events to use. */
4318 copy_v2_v2_int(win->eventstate->prev_xy, event->xy);
4319
4320 /* Un-link and free here, Blender-quit then frees all. */
4321 BLI_remlink(&win->runtime->event_queue, event);
4322 wm_event_free_last_handled(win, event);
4323 }
4324
4325 /* Only add mouse-move when the event queue was read entirely. */
4326 if (win->addmousemove && win->eventstate) {
4327 wmEvent tevent = *(win->eventstate);
4328 // printf("adding MOUSEMOVE %d %d\n", tevent.xy[0], tevent.xy[1]);
4329 tevent.type = MOUSEMOVE;
4330 tevent.val = KM_NOTHING;
4331 tevent.prev_xy[0] = tevent.xy[0];
4332 tevent.prev_xy[1] = tevent.xy[1];
4333 tevent.flag = (eWM_EventFlag)0;
4334 wm_event_add_intern(win, &tevent);
4335 win->addmousemove = 0;
4336 }
4337
4338 CTX_wm_window_set(C, nullptr);
4339 }
4340
4341 /* Update key configuration after handling events. */
4344
4345 /* End GPU render boundary. Certain event handlers require GPU usage. */
4347}
4348
4350
4351/* -------------------------------------------------------------------- */
4354
4355void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, const int eventval)
4356{
4357 /* Add to all windows! */
4358 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
4359 wmEvent event = *win->eventstate;
4360
4361 event.type = EVT_FILESELECT;
4362 event.val = eventval;
4363 event.flag = (eWM_EventFlag)0;
4364 event.customdata = ophandle; /* Only as void pointer type check. */
4365
4366 WM_event_add(win, &event);
4367 }
4368}
4369
4389{
4390 wmWindow *ctx_win = CTX_wm_window(C);
4391
4392 for (wmWindow *ctx_win_or_parent = ctx_win; ctx_win_or_parent;
4393 ctx_win_or_parent = ctx_win_or_parent->parent)
4394 {
4395 ScrArea *file_area = ED_fileselect_handler_area_find_any_with_op(ctx_win_or_parent);
4396
4397 if (!file_area) {
4398 return ctx_win_or_parent;
4399 }
4400
4401 if (file_area->full) {
4402 return ctx_win_or_parent;
4403 }
4404 }
4405
4406 /* Fall back to the first window. */
4407 const wmWindowManager *wm = CTX_wm_manager(C);
4409 static_cast<const wmWindow *>(wm->windows.first)));
4410 return static_cast<wmWindow *>(wm->windows.first);
4411}
4412
4413/* Operator is supposed to have a filled "path" property. */
4414/* Optional property: file-type (XXX enum?) */
4415
4417{
4419 wmWindow *ctx_win = CTX_wm_window(C);
4420
4421 /* The following vars define the root context. That is essentially the "parent" context of the
4422 * File Browser operation, to be restored for eventually executing the file operation. */
4424 /* Determined later. */
4425 ScrArea *root_area = nullptr;
4426 ARegion *root_region = nullptr;
4427
4428 if (!G.quiet) {
4429 /* Perform some sanity checks.
4430 *
4431 * - Using the file-path sub-types is important because it's possible paths don't use
4432 * UTF8 compatible strings, the Python API only accounts for this for "path" sub-types.
4433 *
4434 * - The sub-types in the messages match the Python ID's
4435 * since this it's most likely Python developers will be encountering these messages.
4436 *
4437 * - These could be made into errors however that would break existing scripts.
4438 */
4439 const char *prefix = "fileselect_add";
4440 PropertyRNA *prop;
4441 const char *prop_id;
4442
4443 prop_id = "filepath";
4444 prop = RNA_struct_find_property(op->ptr, prop_id);
4445 if (prop) {
4446 if (!((RNA_property_type(prop) == PROP_STRING) &&
4448 {
4449 printf("%s: \"%s\" expected a string with a 'FILE_PATH' subtype.\n", prefix, prop_id);
4450 }
4451 }
4452 prop_id = "directory";
4453 prop = RNA_struct_find_property(op->ptr, prop_id);
4454 if (prop) {
4455 if (!((RNA_property_type(prop) == PROP_STRING) &&
4457 {
4458 printf("%s: \"%s\" expected a string with a 'DIR_PATH' subtype.\n", prefix, prop_id);
4459 }
4460 }
4461
4462 prop_id = "filename";
4463 prop = RNA_struct_find_property(op->ptr, prop_id);
4464 if (prop) {
4465 if (!((RNA_property_type(prop) == PROP_STRING) &&
4467 {
4468 printf("%s: \"%s\" expected a string with a 'FILE_NAME' subtype.\n", prefix, prop_id);
4469 }
4470 }
4471
4472 /* Other methods could be checked too `files`, `check_existing`, `filter_glob`... etc. */
4473 }
4474
4475 /* Setting the context window unsets the context area & screen. Avoid doing that, so operators
4476 * calling the file browser can operate in the context the browser was opened in. */
4477 if (ctx_win != root_win) {
4478 CTX_wm_window_set(C, root_win);
4479 }
4480
4481 /* The root window may already have a File Browser open. Cancel it if so, only 1 should be open
4482 * per window. The root context of this operation is also used for the new operation. */
4483 LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, &root_win->modalhandlers) {
4484 if (handler_base->type == WM_HANDLER_TYPE_OP) {
4485 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
4486 if (handler->is_fileselect == false) {
4487 continue;
4488 }
4489
4491 C, handler, ctx_win->eventstate, &root_area, &root_region);
4492
4493 ScrArea *file_area = ED_fileselect_handler_area_find(root_win, handler->op);
4494
4495 if (file_area) {
4496 CTX_wm_area_set(C, file_area);
4498 }
4499 /* If not found we stop the handler without changing the screen. */
4500 else {
4502 C, &root_win->modalhandlers, handler, EVT_FILESELECT_EXTERNAL_CANCEL);
4503 }
4504 }
4505 }
4506
4507 BLI_assert(root_win != nullptr);
4508 /* When not reusing the root context from a previous file browsing operation, use the current
4509 * area & region, if they are inside the root window. */
4510 if (!root_area && ctx_win == root_win) {
4511 root_area = CTX_wm_area(C);
4512 root_region = CTX_wm_region(C);
4513 }
4514
4516 handler->head.type = WM_HANDLER_TYPE_OP;
4517
4518 handler->is_fileselect = true;
4519 handler->op = op;
4520 handler->context.win = root_win;
4521 handler->context.area = root_area;
4522 handler->context.region = root_region;
4523
4524 wm_handler_operator_insert(root_win, handler);
4525
4526 /* Check props once before invoking if check is available
4527 * ensures initial properties are valid. */
4528 if (op->type->check) {
4529 op->type->check(C, op); /* Ignore return value. */
4530 }
4531
4533
4534 if (ctx_win != root_win) {
4535 CTX_wm_window_set(C, ctx_win);
4536 }
4537}
4538
4540
4541/* -------------------------------------------------------------------- */
4544
4549 char id[0];
4550};
4551
4552void *WM_event_consecutive_data_get(wmWindow *win, const char *id)
4553{
4555 if (cdata && STREQ(cdata->id, id)) {
4556 return cdata->custom_data;
4557 }
4558 return nullptr;
4559}
4560
4561void WM_event_consecutive_data_set(wmWindow *win, const char *id, void *custom_data)
4562{
4565 }
4566
4567 const size_t id_size = strlen(id) + 1;
4568 wmEvent_ConsecutiveData *cdata = static_cast<wmEvent_ConsecutiveData *>(
4569 MEM_mallocN(sizeof(*cdata) + id_size, __func__));
4570 cdata->custom_data = custom_data;
4571 memcpy((cdata + 1), id, id_size);
4573}
4574
4576{
4578 if (cdata == nullptr) {
4579 return;
4580 }
4581
4582 if (cdata->custom_data) {
4583 MEM_freeN(cdata->custom_data);
4584 }
4585 MEM_freeN(cdata);
4587}
4588
4590
4591/* -------------------------------------------------------------------- */
4594
4595#if 0
4596/* Lets not expose struct outside wm? */
4597static void WM_event_set_handler_flag(wmEventHandler *handler, const int flag)
4598{
4599 handler->flag = flag;
4600}
4601#endif
4602
4604 ScrArea *area,
4605 ARegion *region,
4606 wmOperator *op)
4607{
4609 handler->head.type = WM_HANDLER_TYPE_OP;
4610
4611 /* Operator was part of macro. */
4612 if (op->opm) {
4613 /* Give the mother macro to the handler. */
4614 handler->op = op->opm;
4615 /* Mother macro `opm` becomes the macro element. */
4616 handler->op->opm = op;
4617 }
4618 else {
4619 handler->op = op;
4620 }
4621
4622 handler->context.area = area; /* Means frozen screen context for modal handlers! */
4623 handler->context.region = region;
4624 handler->context.region_type = handler->context.region ? handler->context.region->regiontype :
4625 -1;
4626
4627 wm_handler_operator_insert(win, handler);
4628
4629 if (op->type->modalkeymap) {
4631 }
4632
4633 return handler;
4634}
4635
4637{
4638 wmWindow *win = CTX_wm_window(C);
4639 ScrArea *area = CTX_wm_area(C);
4640 ARegion *region = CTX_wm_region(C);
4641 return WM_event_add_modal_handler_ex(win, area, region, op);
4642}
4643
4644void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, const bool postpone)
4645{
4646 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
4647 if (handler_base->type == WM_HANDLER_TYPE_OP) {
4648 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
4649 if ((handler->op == op) || (op->opm && (handler->op == op->opm))) {
4650 /* Handlers will be freed in #wm_handlers_do(). */
4651 if (postpone) {
4652 handler->head.flag |= WM_HANDLER_DO_FREE;
4653 }
4654 else {
4655 BLI_remlink(handlers, handler);
4656 wm_event_free_handler(&handler->head);
4657 }
4658 break;
4659 }
4660 }
4661 }
4662}
4663
4664void WM_event_remove_modal_handler_all(const wmOperator *op, const bool postpone)
4665{
4666 Main *bmain = G_MAIN;
4667 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
4668 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
4669 WM_event_remove_model_handler(&win->modalhandlers, op, postpone);
4670 }
4671}
4672
4673void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
4674{
4675 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
4676 if (handler_base->type == WM_HANDLER_TYPE_OP) {
4677 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
4678 /* File-select handler is quite special.
4679 * it needs to keep old area stored in handler, so don't change it. */
4680 if ((handler->context.area == old_area) && (handler->is_fileselect == false)) {
4681 handler->context.area = new_area;
4682 }
4683 }
4684 }
4685}
4686
4688 const ARegion *old_region,
4689 ARegion *new_region)
4690{
4691 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
4692 if (handler_base->type == WM_HANDLER_TYPE_OP) {
4693 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
4694 /* File-select handler is quite special.
4695 * it needs to keep old region stored in handler, so don't change it. */
4696 if ((handler->context.region == old_region) && (handler->is_fileselect == false)) {
4697 handler->context.region = new_region;
4698 handler->context.region_type = new_region ? new_region->regiontype : int(RGN_TYPE_WINDOW);
4699 }
4700 }
4701 }
4702}
4703
4705 const ARegion *old_region,
4706 ARegion *new_region)
4707{
4708 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
4709 if (handler_base->type == WM_HANDLER_TYPE_UI) {
4710 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
4711 if (handler->context.region_popup == old_region) {
4712 handler->context.region_popup = new_region;
4713 }
4714 }
4715 }
4716}
4717
4719{
4720 if (!keymap) {
4721 CLOG_WARN(WM_LOG_HANDLERS, "called with nullptr key-map");
4722 return nullptr;
4723 }
4724
4725 /* Only allow same key-map once. */
4726 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
4727 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
4728 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
4729 if (handler->keymap == keymap) {
4730 return handler;
4731 }
4732 }
4733 }
4734
4736 handler->head.type = WM_HANDLER_TYPE_KEYMAP;
4737 BLI_addtail(handlers, handler);
4738 handler->keymap = keymap;
4739
4740 return handler;
4741}
4742
4756 wmWindow *win,
4757 wmEventHandler_Keymap *handler,
4758 wmEventHandler_KeymapResult *km_result,
4759 /* Extra arguments. */
4760 const bool with_gizmos)
4761{
4762 memset(km_result, 0x0, sizeof(*km_result));
4763
4764 const char *keymap_id_list[BOUNDED_ARRAY_TYPE_SIZE<decltype(km_result->keymaps)>()];
4765 int keymap_id_list_len = 0;
4766
4767 /* NOTE(@ideasman42): If `win` is nullptr, this function may not behave as expected.
4768 * Assert since this should not happen in practice.
4769 * If it does, the window could be looked up in `wm` using the `area`.
4770 * Keep nullptr checks in run-time code since any crashes here are difficult to redo. */
4771 BLI_assert_msg(win != nullptr, "The window should always be set for tool interactions!");
4772 const Scene *scene = win ? win->scene : nullptr;
4773
4774 ScrArea *area = static_cast<ScrArea *>(handler->dynamic.user_data);
4775 handler->keymap_tool = nullptr;
4776 bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : nullptr;
4777
4778 if (tref_rt && tref_rt->keymap[0]) {
4779 keymap_id_list[keymap_id_list_len++] = tref_rt->keymap;
4780 }
4781
4782 bool is_gizmo_visible = false;
4783 bool is_gizmo_highlight = false;
4784
4785 if ((tref_rt && tref_rt->keymap_fallback[0]) &&
4787 {
4788 bool add_keymap = false;
4789 /* Support for the gizmo owning the tool key-map. */
4790
4791 if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) {
4792 add_keymap = true;
4793 }
4794
4795 if (with_gizmos && (tref_rt->gizmo_group[0] != '\0')) {
4796 wmGizmoMap *gzmap = nullptr;
4797 wmGizmoGroup *gzgroup = nullptr;
4798 LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
4799 if (region->runtime->gizmo_map != nullptr) {
4800 gzmap = region->runtime->gizmo_map;
4801 gzgroup = WM_gizmomap_group_find(gzmap, tref_rt->gizmo_group);
4802 if (gzgroup != nullptr) {
4803 break;
4804 }
4805 }
4806 }
4807 if (gzgroup != nullptr) {
4809 /* If all are hidden, don't override. */
4810 is_gizmo_visible = true;
4811 wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap);
4812 if (highlight) {
4813 is_gizmo_highlight = true;
4814 }
4815 add_keymap = true;
4816 }
4817 }
4818 }
4819
4820 if (add_keymap) {
4821 keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback;
4822 }
4823 }
4824
4825 if (is_gizmo_visible && !is_gizmo_highlight) {
4826 if (keymap_id_list_len == 2) {
4827 std::swap(keymap_id_list[0], keymap_id_list[1]);
4828 }
4829 }
4830
4831 for (int i = 0; i < keymap_id_list_len; i++) {
4832 const char *keymap_id = keymap_id_list[i];
4833 BLI_assert(keymap_id && keymap_id[0]);
4834
4836 &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW);
4837 /* We shouldn't use key-maps from unrelated spaces. */
4838 if (km == nullptr) {
4839 printf("Key-map: '%s' not found for tool '%s'\n", keymap_id, area->runtime.tool->idname);
4840 continue;
4841 }
4842 handler->keymap_tool = area->runtime.tool;
4843 km_result->keymaps[km_result->keymaps_len++] = km;
4844 }
4845}
4846
4848 wmWindow *win,
4849 wmEventHandler_Keymap *handler,
4850 wmEventHandler_KeymapResult *km_result)
4851{
4852 wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, true);
4853}
4854
4856 wmWindow *win,
4857 wmEventHandler_Keymap *handler,
4858 wmEventHandler_KeymapResult *km_result)
4859{
4860 wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, false);
4861}
4862
4864 ListBase *handlers, wmEventHandler_KeymapDynamicFn keymap_fn, void *user_data)
4865{
4866 if (!keymap_fn) {
4867 CLOG_WARN(WM_LOG_HANDLERS, "called with nullptr keymap_fn");
4868 return nullptr;
4869 }
4870
4871 /* Only allow same key-map once. */
4872 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
4873 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
4874 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
4875 if (handler->dynamic.keymap_fn == keymap_fn) {
4876 /* Maximizing the view needs to update the area. */
4877 handler->dynamic.user_data = user_data;
4878 return handler;
4879 }
4880 }
4881 }
4882
4884 handler->head.type = WM_HANDLER_TYPE_KEYMAP;
4885 BLI_addtail(handlers, handler);
4886 handler->dynamic.keymap_fn = keymap_fn;
4887 handler->dynamic.user_data = user_data;
4888
4889 return handler;
4890}
4891
4893 wmKeyMap *keymap,
4894 int /*priority*/)
4895{
4896 WM_event_remove_keymap_handler(handlers, keymap);
4897
4898 wmEventHandler_Keymap *handler = MEM_callocN<wmEventHandler_Keymap>("event key-map handler");
4899 handler->head.type = WM_HANDLER_TYPE_KEYMAP;
4900
4901 BLI_addhead(handlers, handler);
4902 handler->keymap = keymap;
4903
4904 return handler;
4905}
4906
4907static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect)
4908{
4909 if (BLI_rcti_isect_pt_v(rect, event->xy)) {
4910 return true;
4911 }
4912 if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(rect, event->prev_xy)) {
4913 return true;
4914 }
4915 return false;
4916}
4917
4919 const ScrArea * /*area*/,
4920 const ARegion *region,
4921 const wmEvent *event)
4922{
4923 rcti rect = region->v2d.mask;
4924 BLI_rcti_translate(&rect, region->winrct.xmin, region->winrct.ymin);
4925 return event_or_prev_in_rect(event, &rect);
4926}
4927
4929 const ScrArea *area,
4930 const ARegion *region,
4931 const wmEvent *event)
4932{
4933 switch (area->spacetype) {
4934 case SPACE_ACTION: {
4935 const SpaceAction *saction = static_cast<SpaceAction *>(area->spacedata.first);
4936 if ((saction->flag & SACTION_SHOW_MARKERS) == 0) {
4937 return false;
4938 }
4939 break;
4940 }
4941 case SPACE_GRAPH: {
4942 const SpaceGraph *sgraph = static_cast<SpaceGraph *>(area->spacedata.first);
4943 if ((sgraph->flag & SIPO_SHOW_MARKERS) == 0) {
4944 return false;
4945 }
4946 break;
4947 }
4948 case SPACE_NLA: {
4949 const SpaceNla *snla = static_cast<SpaceNla *>(area->spacedata.first);
4950 if ((snla->flag & SNLA_SHOW_MARKERS) == 0) {
4951 return false;
4952 }
4953 break;
4954 }
4955 case SPACE_SEQ: {
4956 const SpaceSeq *seq = static_cast<SpaceSeq *>(area->spacedata.first);
4957 if ((seq->flag & SEQ_SHOW_MARKERS) == 0) {
4958 return false;
4959 }
4960 break;
4961 }
4962 default:
4963 break;
4964 }
4965
4967 const_cast<ScrArea *>(area));
4968 if (BLI_listbase_is_empty(markers)) {
4969 return false;
4970 }
4971
4972 rcti rect = region->winrct;
4973 rect.ymax = rect.ymin + UI_MARKER_MARGIN_Y;
4974 /* TODO: investigate returning `event_or_prev_in_rect(event, &rect)` here.
4975 * The difference is subtle but correct so dragging away from the region works. */
4976 return BLI_rcti_isect_pt_v(&rect, event->xy);
4977}
4978
4980 const ScrArea *area,
4981 const ARegion *region,
4982 const wmEvent *event)
4983{
4984 if (!WM_event_handler_region_v2d_mask_poll(win, area, region, event)) {
4985 return false;
4986 }
4987 /* Casting away `const` is only needed for a non-constant return value. */
4989 const_cast<ScrArea *>(area));
4990 if (markers && !BLI_listbase_is_empty(markers)) {
4991 return !WM_event_handler_region_marker_poll(win, area, region, event);
4992 }
4993 return true;
4994}
4995
4997 wmKeyMap *keymap,
4998 EventHandlerPoll poll)
4999{
5000 wmEventHandler_Keymap *handler = WM_event_add_keymap_handler(handlers, keymap);
5001 if (handler == nullptr) {
5002 return nullptr;
5003 }
5004
5005 handler->head.poll = poll;
5006 return handler;
5007}
5008
5013
5015{
5016 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
5017 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
5018 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
5019 if (handler->keymap == keymap) {
5020 BLI_remlink(handlers, handler);
5021 wm_event_free_handler(&handler->head);
5022 break;
5023 }
5024 }
5025 }
5026}
5027
5029 void(keymap_tag)(wmKeyMap *keymap,
5030 wmKeyMapItem *kmi,
5031 void *user_data),
5032 void *user_data)
5033{
5034 handler->post.post_fn = keymap_tag;
5035 handler->post.user_data = user_data;
5036}
5037
5039 ListBase *handlers,
5040 wmUIHandlerFunc handle_fn,
5041 wmUIHandlerRemoveFunc remove_fn,
5042 void *user_data,
5044{
5046 handler->head.type = WM_HANDLER_TYPE_UI;
5047 handler->handle_fn = handle_fn;
5048 handler->remove_fn = remove_fn;
5049 handler->user_data = user_data;
5050 if (C) {
5051 handler->context.area = CTX_wm_area(C);
5052 handler->context.region = CTX_wm_region(C);
5054 }
5055 else {
5056 handler->context.area = nullptr;
5057 handler->context.region = nullptr;
5058 handler->context.region_popup = nullptr;
5059 }
5060
5062 handler->head.flag = flag;
5063
5064 BLI_addhead(handlers, handler);
5065
5066 return handler;
5067}
5068
5070 wmUIHandlerFunc handle_fn,
5071 wmUIHandlerRemoveFunc remove_fn,
5072 void *user_data,
5073 const bool postpone)
5074{
5075 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
5076 if (handler_base->type == WM_HANDLER_TYPE_UI) {
5077 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
5078 if ((handler->handle_fn == handle_fn) && (handler->remove_fn == remove_fn) &&
5079 (handler->user_data == user_data))
5080 {
5081 /* Handlers will be freed in #wm_handlers_do(). */
5082 if (postpone) {
5083 handler->head.flag |= WM_HANDLER_DO_FREE;
5084 }
5085 else {
5086 BLI_remlink(handlers, handler);
5087 wm_event_free_handler(&handler->head);
5088 }
5089 break;
5090 }
5091 }
5092 }
5093}
5094
5096 ListBase *handlers,
5097 wmUIHandlerFunc handle_fn,
5098 wmUIHandlerRemoveFunc remove_fn)
5099{
5100 LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, handlers) {
5101 if (handler_base->type == WM_HANDLER_TYPE_UI) {
5102 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
5103 if ((handler->handle_fn == handle_fn) && (handler->remove_fn == remove_fn)) {
5104 remove_fn(C, handler->user_data);
5105 BLI_remlink(handlers, handler);
5106 wm_event_free_handler(&handler->head);
5107 }
5108 }
5109 }
5110}
5111
5113{
5114 /* Only allow same dropbox once. */
5115 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
5116 if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
5117 wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
5118 if (handler->dropboxes == dropboxes) {
5119 return handler;
5120 }
5121 }
5122 }
5123
5126
5127 /* Dropbox stored static, no free or copy. */
5128 handler->dropboxes = dropboxes;
5129 BLI_addhead(handlers, handler);
5130
5131 return handler;
5132}
5133
5134void WM_event_remove_area_handler(ListBase *handlers, void *area)
5135{
5136 /* XXX(@ton): solution works, still better check the real cause. */
5137
5138 LISTBASE_FOREACH_MUTABLE (wmEventHandler *, handler_base, handlers) {
5139 if (handler_base->type == WM_HANDLER_TYPE_UI) {
5140 wmEventHandler_UI *handler = (wmEventHandler_UI *)handler_base;
5141 if (handler->context.area == area) {
5142 BLI_remlink(handlers, handler);
5143 wm_event_free_handler(handler_base);
5144 }
5145 }
5146 }
5147}
5148
5150{
5151 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
5152 if (handler_base->type != WM_HANDLER_TYPE_OP) {
5153 continue;
5154 }
5155 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
5156 if (handler->op && handler->op->type == ot) {
5157 return handler->op;
5158 }
5159 }
5160 return nullptr;
5161}
5162
5163#if 0
5164static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
5165{
5166 BLI_remlink(handlers, handler);
5167 wm_event_free_handler(handler);
5168}
5169#endif
5170
5172{
5173 win->addmousemove = 1;
5174}
5175
5177
5178/* -------------------------------------------------------------------- */
5181
5182#ifdef WITH_INPUT_NDOF
5186static wmEventType wm_event_type_from_ndof_button(GHOST_NDOF_ButtonT button)
5187{
5188# define CASE_NDOF_BUTTON(button) \
5189 case GHOST_NDOF_BUTTON_##button: \
5190 return NDOF_BUTTON_##button
5191
5192# define CASE_NDOF_BUTTON_IGNORE(button) \
5193 case GHOST_NDOF_BUTTON_##button: \
5194 break;
5195
5196 switch (button) {
5197 CASE_NDOF_BUTTON(MENU);
5198 CASE_NDOF_BUTTON(FIT);
5199 CASE_NDOF_BUTTON(TOP);
5200 CASE_NDOF_BUTTON(LEFT);
5201 CASE_NDOF_BUTTON(RIGHT);
5202 CASE_NDOF_BUTTON(FRONT);
5203 CASE_NDOF_BUTTON(BOTTOM);
5204 CASE_NDOF_BUTTON(BACK);
5205 CASE_NDOF_BUTTON(ROLL_CW);
5206 CASE_NDOF_BUTTON(ROLL_CCW);
5207 CASE_NDOF_BUTTON(ISO1);
5208 CASE_NDOF_BUTTON(ISO2);
5209 CASE_NDOF_BUTTON(1);
5210 CASE_NDOF_BUTTON(2);
5211 CASE_NDOF_BUTTON(3);
5212 CASE_NDOF_BUTTON(4);
5213 CASE_NDOF_BUTTON(5);
5214 CASE_NDOF_BUTTON(6);
5215 CASE_NDOF_BUTTON(7);
5216 CASE_NDOF_BUTTON(8);
5217 CASE_NDOF_BUTTON(9);
5218 CASE_NDOF_BUTTON(10);
5219 CASE_NDOF_BUTTON(11);
5220 CASE_NDOF_BUTTON(12);
5221 CASE_NDOF_BUTTON(ROTATE);
5222 CASE_NDOF_BUTTON(PANZOOM);
5223 CASE_NDOF_BUTTON(DOMINANT);
5224 CASE_NDOF_BUTTON(PLUS);
5225 CASE_NDOF_BUTTON(MINUS);
5226 CASE_NDOF_BUTTON(SPIN_CW);
5227 CASE_NDOF_BUTTON(SPIN_CCW);
5228 CASE_NDOF_BUTTON(TILT_CW);
5229 CASE_NDOF_BUTTON(TILT_CCW);
5230 CASE_NDOF_BUTTON(V1);
5231 CASE_NDOF_BUTTON(V2);
5232 CASE_NDOF_BUTTON(V3);
5233 CASE_NDOF_BUTTON(SAVE_V1);
5234 CASE_NDOF_BUTTON(SAVE_V2);
5235 CASE_NDOF_BUTTON(SAVE_V3);
5236
5237 /* Disabled as GHOST converts these to keyboard events
5238 * which use regular keyboard event handling logic. */
5239 /* Keyboard emulation. */
5240 CASE_NDOF_BUTTON_IGNORE(ESC);
5241 CASE_NDOF_BUTTON_IGNORE(ENTER);
5242 CASE_NDOF_BUTTON_IGNORE(DELETE);
5243 CASE_NDOF_BUTTON_IGNORE(TAB);
5244 CASE_NDOF_BUTTON_IGNORE(SPACE);
5245 CASE_NDOF_BUTTON_IGNORE(ALT);
5246 CASE_NDOF_BUTTON_IGNORE(SHIFT);
5247 CASE_NDOF_BUTTON_IGNORE(CTRL);
5248
5249 CASE_NDOF_BUTTON_IGNORE(KBP_F1);
5250 CASE_NDOF_BUTTON_IGNORE(KBP_F2);
5251 CASE_NDOF_BUTTON_IGNORE(KBP_F3);
5252 CASE_NDOF_BUTTON_IGNORE(KBP_F4);
5253 CASE_NDOF_BUTTON_IGNORE(KBP_F5);
5254 CASE_NDOF_BUTTON_IGNORE(KBP_F6);
5255 CASE_NDOF_BUTTON_IGNORE(KBP_F7);
5256 CASE_NDOF_BUTTON_IGNORE(KBP_F8);
5257 CASE_NDOF_BUTTON_IGNORE(KBP_F9);
5258 CASE_NDOF_BUTTON_IGNORE(KBP_F10);
5259 CASE_NDOF_BUTTON_IGNORE(KBP_F11);
5260 CASE_NDOF_BUTTON_IGNORE(KBP_F12);
5261
5262 CASE_NDOF_BUTTON_IGNORE(NP_F1);
5263 CASE_NDOF_BUTTON_IGNORE(NP_F2);
5264 CASE_NDOF_BUTTON_IGNORE(NP_F3);
5265 CASE_NDOF_BUTTON_IGNORE(NP_F4);
5266
5267 /* Quiet switch warnings. */
5268 CASE_NDOF_BUTTON_IGNORE(NONE);
5269 CASE_NDOF_BUTTON_IGNORE(INVALID);
5270 CASE_NDOF_BUTTON_IGNORE(USER);
5271 }
5272
5273# undef CASE_NDOF_BUTTON
5274# undef CASE_NDOF_BUTTON_IGNORE
5275
5276 CLOG_WARN(WM_LOG_EVENTS, "unknown event type %d from ndof button", int(button));
5277 return EVENT_NONE;
5278}
5279
5280#endif /* WITH_INPUT_NDOF */
5281
5286{
5287 if (key >= GHOST_kKeyA && key <= GHOST_kKeyZ) {
5288 return wmEventType(EVT_AKEY + (int(key) - GHOST_kKeyA));
5289 }
5290 if (key >= GHOST_kKey0 && key <= GHOST_kKey9) {
5291 return wmEventType(EVT_ZEROKEY + (int(key) - GHOST_kKey0));
5292 }
5293 if (key >= GHOST_kKeyNumpad0 && key <= GHOST_kKeyNumpad9) {
5294 return wmEventType(EVT_PAD0 + (int(key) - GHOST_kKeyNumpad0));
5295 }
5296 if (key >= GHOST_kKeyF1 && key <= GHOST_kKeyF24) {
5297 return wmEventType(EVT_F1KEY + (int(key) - GHOST_kKeyF1));
5298 }
5299
5300 switch (key) {
5302 return EVT_BACKSPACEKEY;
5303 case GHOST_kKeyTab:
5304 return EVT_TABKEY;
5305 case GHOST_kKeyLinefeed:
5306 return EVT_LINEFEEDKEY;
5307 case GHOST_kKeyClear:
5308 return EVENT_NONE;
5309 case GHOST_kKeyEnter:
5310 return EVT_RETKEY;
5311
5312 case GHOST_kKeyEsc:
5313 return EVT_ESCKEY;
5314 case GHOST_kKeySpace:
5315 return EVT_SPACEKEY;
5316 case GHOST_kKeyQuote:
5317 return EVT_QUOTEKEY;
5318 case GHOST_kKeyComma:
5319 return EVT_COMMAKEY;
5320 case GHOST_kKeyMinus:
5321 return EVT_MINUSKEY;
5322 case GHOST_kKeyPlus:
5323 return EVT_PLUSKEY;
5324 case GHOST_kKeyPeriod:
5325 return EVT_PERIODKEY;
5326 case GHOST_kKeySlash:
5327 return EVT_SLASHKEY;
5328
5330 return EVT_SEMICOLONKEY;
5331 case GHOST_kKeyEqual:
5332 return EVT_EQUALKEY;
5333
5335 return EVT_LEFTBRACKETKEY;
5337 return EVT_RIGHTBRACKETKEY;
5339 return EVT_BACKSLASHKEY;
5341 return EVT_ACCENTGRAVEKEY;
5342
5344 return EVT_LEFTSHIFTKEY;
5346 return EVT_RIGHTSHIFTKEY;
5348 return EVT_LEFTCTRLKEY;
5350 return EVT_RIGHTCTRLKEY;
5351 case GHOST_kKeyLeftOS:
5352 case GHOST_kKeyRightOS:
5353 return EVT_OSKEY;
5356 return EVT_HYPER;
5357 case GHOST_kKeyLeftAlt:
5358 return EVT_LEFTALTKEY;
5359 case GHOST_kKeyRightAlt:
5360 return EVT_RIGHTALTKEY;
5361 case GHOST_kKeyApp:
5362 return EVT_APPKEY;
5363
5364 case GHOST_kKeyCapsLock:
5365 return EVT_CAPSLOCKKEY;
5366 case GHOST_kKeyNumLock:
5367 return EVENT_NONE;
5369 return EVENT_NONE;
5370
5372 return EVT_LEFTARROWKEY;
5374 return EVT_RIGHTARROWKEY;
5375 case GHOST_kKeyUpArrow:
5376 return EVT_UPARROWKEY;
5378 return EVT_DOWNARROWKEY;
5379
5381 return EVENT_NONE;
5382 case GHOST_kKeyPause:
5383 return EVT_PAUSEKEY;
5384
5385 case GHOST_kKeyInsert:
5386 return EVT_INSERTKEY;
5387 case GHOST_kKeyDelete:
5388 return EVT_DELKEY;
5389 case GHOST_kKeyHome:
5390 return EVT_HOMEKEY;
5391 case GHOST_kKeyEnd:
5392 return EVT_ENDKEY;
5393 case GHOST_kKeyUpPage:
5394 return EVT_PAGEUPKEY;
5395 case GHOST_kKeyDownPage:
5396 return EVT_PAGEDOWNKEY;
5397
5399 return EVT_PADPERIOD;
5401 return EVT_PADENTER;
5403 return EVT_PADPLUSKEY;
5405 return EVT_PADMINUS;
5407 return EVT_PADASTERKEY;
5409 return EVT_PADSLASHKEY;
5410
5411 case GHOST_kKeyGrLess:
5412 return EVT_GRLESSKEY;
5413
5415 return EVT_MEDIAPLAY;
5417 return EVT_MEDIASTOP;
5419 return EVT_MEDIAFIRST;
5421 return EVT_MEDIALAST;
5422
5423 case GHOST_kKeyUnknown:
5424 return EVT_UNKNOWNKEY;
5425
5426#if defined(__GNUC__) || defined(__clang__)
5427 /* Ensure all members of this enum are handled, otherwise generate a compiler warning.
5428 * Note that these members have been handled, these ranges are to satisfy the compiler. */
5429 case GHOST_kKeyF1 ... GHOST_kKeyF24:
5430 case GHOST_kKeyA ... GHOST_kKeyZ:
5432 case GHOST_kKey0 ... GHOST_kKey9: {
5434 break;
5435 }
5436#else
5437 default: {
5438 break;
5439 }
5440#endif
5441 }
5442
5443 CLOG_WARN(WM_LOG_EVENTS, "unknown event type %d from ghost", int(key));
5444 return EVENT_NONE;
5445}
5446
5451 const wmEventType fallback)
5452{
5453#define CASE_BUTTON(ghost_button, type) \
5454 case ghost_button: \
5455 return type
5456
5457 switch (button) {
5465 case GHOST_kButtonMaskNone: {
5467 }
5468 }
5469
5470#undef CASE_BUTTON
5471
5473 return fallback;
5474}
5475
5476static void wm_eventemulation(wmEvent *event, bool test_only)
5477{
5478 /* Store last middle-mouse event value to make emulation work
5479 * when modifier keys are released first.
5480 * This really should be in a data structure somewhere. */
5481 static int emulating_event = EVENT_NONE;
5482
5483 /* Middle-mouse emulation. */
5484 if (U.flag & USER_TWOBUTTONMOUSE) {
5485
5486 if (event->type == LEFTMOUSE) {
5487 const wmEventModifierFlag mod_test = (
5488#if !defined(WIN32)
5489 (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? KM_OSKEY : KM_ALT
5490#else
5491 /* Disable for WIN32 for now because it accesses the start menu. */
5492 KM_ALT
5493#endif
5494 );
5495
5496 if (event->val == KM_PRESS) {
5497 if (event->modifier & mod_test) {
5498 event->modifier &= ~mod_test;
5499 event->type = MIDDLEMOUSE;
5500
5501 if (!test_only) {
5502 emulating_event = MIDDLEMOUSE;
5503 }
5504 }
5505 }
5506 else if (event->val == KM_RELEASE) {
5507 /* Only send middle-mouse release if emulated. */
5508 if (emulating_event == MIDDLEMOUSE) {
5509 event->type = MIDDLEMOUSE;
5510 event->modifier &= ~mod_test;
5511 }
5512
5513 if (!test_only) {
5514 emulating_event = EVENT_NONE;
5515 }
5516 }
5517 }
5518 }
5519
5520 /* Numeric-pad emulation. */
5521 if (U.flag & USER_NONUMPAD) {
5522 switch (event->type) {
5523 case EVT_ZEROKEY:
5524 event->type = EVT_PAD0;
5525 break;
5526 case EVT_ONEKEY:
5527 event->type = EVT_PAD1;
5528 break;
5529 case EVT_TWOKEY:
5530 event->type = EVT_PAD2;
5531 break;
5532 case EVT_THREEKEY:
5533 event->type = EVT_PAD3;
5534 break;
5535 case EVT_FOURKEY:
5536 event->type = EVT_PAD4;
5537 break;
5538 case EVT_FIVEKEY:
5539 event->type = EVT_PAD5;
5540 break;
5541 case EVT_SIXKEY:
5542 event->type = EVT_PAD6;
5543 break;
5544 case EVT_SEVENKEY:
5545 event->type = EVT_PAD7;
5546 break;
5547 case EVT_EIGHTKEY:
5548 event->type = EVT_PAD8;
5549 break;
5550 case EVT_NINEKEY:
5551 event->type = EVT_PAD9;
5552 break;
5553 case EVT_MINUSKEY:
5554 event->type = EVT_PADMINUS;
5555 break;
5556 case EVT_EQUALKEY:
5557 event->type = EVT_PADPLUSKEY;
5558 break;
5559 case EVT_BACKSLASHKEY:
5560 event->type = EVT_PADSLASHKEY;
5561 break;
5562 default: {
5563 break;
5564 }
5565 }
5566 }
5567}
5568
5570{
5571 wmTabletData tablet_data{};
5572 tablet_data.active = EVT_TABLET_NONE;
5573 tablet_data.pressure = 1.0f;
5574 tablet_data.tilt.x = 0.0f;
5575 tablet_data.tilt.y = 0.0f;
5576 tablet_data.is_motion_absolute = false;
5577 return tablet_data;
5578}
5579
5581{
5582 *tablet_data = wm_event_tablet_data_default();
5583}
5584
5586{
5587 if ((tablet_data != nullptr) && tablet_data->Active != GHOST_kTabletModeNone) {
5588 wmtab->active = int(tablet_data->Active);
5589 wmtab->pressure = wm_pressure_curve(tablet_data->Pressure);
5590 wmtab->tilt = blender::float2(tablet_data->Xtilt, tablet_data->Ytilt);
5591 /* We could have a preference to support relative tablet motion (we can't detect that). */
5592 wmtab->is_motion_absolute = true;
5593 // printf("%s: using tablet %.5f\n", __func__, wmtab->pressure);
5594 }
5595 else {
5597 // printf("%s: not using tablet\n", __func__);
5598 }
5599}
5600
5601#ifdef WITH_INPUT_NDOF
5602/* Adds custom-data to event. */
5603static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *ghost)
5604{
5605 wmNDOFMotionData *data = MEM_callocN<wmNDOFMotionData>("Custom-data NDOF");
5606
5607 const float ts = U.ndof_sensitivity;
5608 const float rs = U.ndof_orbit_sensitivity;
5609
5610 mul_v3_v3fl(data->tvec, &ghost->tx, ts);
5611 mul_v3_v3fl(data->rvec, &ghost->rx, rs);
5612
5613 data->dt = ghost->dt;
5614
5615 data->progress = (wmProgress)ghost->progress;
5616
5617 event->custom = EVT_DATA_NDOF_MOTION;
5618 event->customdata = data;
5619 event->customdata_free = true;
5620}
5621#endif /* WITH_INPUT_NDOF */
5622
5623/* Imperfect but probably usable... draw/enable drags to other windows. */
5625{
5626 /* If GHOST doesn't support window positioning, don't use this feature at all. */
5627 const static int8_t supports_window_position = (WM_capabilities_flag() &
5629 if (!supports_window_position) {
5630 return nullptr;
5631 }
5632
5633 if (wm->windows.first == wm->windows.last) {
5634 return nullptr;
5635 }
5636
5637 /* In order to use window size and mouse position (pixels), we have to use a WM function. */
5638
5639 /* Check if outside, include top window bar. */
5640 const blender::int2 win_size = WM_window_native_pixel_size(win);
5641 int event_xy[2] = {UNPACK2(event->xy)};
5642 if (event_xy[0] < 0 || event_xy[1] < 0 || event_xy[0] > win_size[0] ||
5643 event_xy[1] > win_size[1] + 30)
5644 {
5645 /* Let's skip windows having modal handlers now. */
5646 /* Potential XXX ugly... I wouldn't have added a `modalhandlers` list
5647 * (introduced in rev 23331, ton). */
5648 LISTBASE_FOREACH (wmEventHandler *, handler, &win->modalhandlers) {
5649 if (ELEM(handler->type, WM_HANDLER_TYPE_UI, WM_HANDLER_TYPE_OP)) {
5650 return nullptr;
5651 }
5652 }
5653
5654 wmWindow *win_other = WM_window_find_under_cursor(win, event_xy, event_xy);
5655 if (win_other && win_other != win) {
5656 copy_v2_v2_int(event->xy, event_xy);
5657 return win_other;
5658 }
5659 }
5660 return nullptr;
5661}
5662
5663static bool wm_event_is_double_click(const wmEvent *event,
5664 const uint64_t event_time_ms,
5665 const uint64_t event_prev_press_time_ms)
5666{
5667 if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) &&
5668 (event->val == KM_PRESS))
5669 {
5670 if (ISMOUSE_BUTTON(event->type) && WM_event_drag_test(event, event->prev_press_xy)) {
5671 /* Pass. */
5672 }
5673 else {
5674 if ((event_time_ms - event_prev_press_time_ms) < uint64_t(U.dbl_click_time)) {
5675 return true;
5676 }
5677 }
5678 }
5679
5680 return false;
5681}
5682
5686static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state)
5687{
5688 event->prev_val = event_state->prev_val = event_state->val;
5689 event->prev_type = event_state->prev_type = event_state->type;
5690}
5691
5692static void wm_event_prev_click_set(uint64_t event_time_ms,
5693 wmEvent *event_state,
5694 uint64_t *r_event_state_prev_press_time_ms)
5695{
5696 event_state->prev_press_type = event_state->type;
5697 event_state->prev_press_modifier = event_state->modifier;
5698 event_state->prev_press_keymodifier = event_state->keymodifier;
5699 event_state->prev_press_xy[0] = event_state->xy[0];
5700 event_state->prev_press_xy[1] = event_state->xy[1];
5701 *r_event_state_prev_press_time_ms = event_time_ms;
5702}
5703
5705{
5706 wmEvent *event_last = static_cast<wmEvent *>(win->runtime->event_queue.last);
5707
5708 /* Some painting operators want accurate mouse events, they can
5709 * handle in between mouse move moves, others can happily ignore
5710 * them for better performance. */
5711 if (event_last && event_last->type == MOUSEMOVE) {
5712 event_last->type = INBETWEEN_MOUSEMOVE;
5713 event_last->flag = (eWM_EventFlag)0;
5714 }
5715
5716 wmEvent *event_new = wm_event_add_intern(win, event);
5717 if (event_last == nullptr) {
5718 event_last = win->eventstate;
5719 }
5720
5721 copy_v2_v2_int(event_new->prev_xy, event_last->xy);
5722 return event_new;
5723}
5724
5726{
5727 /* Use the last handled event instead of `win->eventstate` because the state of the modifiers
5728 * and previous values should be set based on the last state, not using values from the future.
5729 * So this gives an accurate simulation of mouse motion before the next event is handled. */
5730 const wmEvent *event_last = win->event_last_handled;
5731
5732 wmEvent tevent;
5733 if (event_last) {
5734 tevent = *event_last;
5735
5736 tevent.flag = (eWM_EventFlag)0;
5737 tevent.utf8_buf[0] = '\0';
5738
5739 wm_event_custom_clear(&tevent);
5740 }
5741 else {
5742 memset(&tevent, 0x0, sizeof(tevent));
5743 }
5744
5745 tevent.type = MOUSEMOVE;
5746 tevent.val = KM_NOTHING;
5747 copy_v2_v2_int(tevent.prev_xy, tevent.xy);
5748
5749 wmEvent *event_new = wm_event_add_intern(win, &tevent);
5750 BLI_remlink(&win->runtime->event_queue, event_new);
5751 BLI_addhead(&win->runtime->event_queue, event_new);
5752
5753 return event_new;
5754}
5755
5756static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int deltax, int deltay)
5757{
5758 /* Ignore in between trackpad events for performance, we only need high accuracy
5759 * for painting with mouse moves, for navigation using the accumulated value is ok. */
5760 const wmEvent *event_last = static_cast<wmEvent *>(win->runtime->event_queue.last);
5761 if (event_last && event_last->type == event->type) {
5762 deltax += event_last->xy[0] - event_last->prev_xy[0];
5763 deltay += event_last->xy[1] - event_last->prev_xy[1];
5764
5765 wm_event_free_last(win);
5766 }
5767
5768 /* Set prev_xy, the delta is computed from this in operators. */
5769 wmEvent *event_new = wm_event_add_intern(win, event);
5770 event_new->prev_xy[0] = event_new->xy[0] - deltax;
5771 event_new->prev_xy[1] = event_new->xy[1] - deltay;
5772
5773 return event_new;
5774}
5775
5783 const uint64_t event_time_ms,
5784 wmEvent *event_state,
5785 uint64_t *event_state_prev_press_time_ms_p,
5786 const bool is_keyboard,
5787 const bool check_double_click)
5788{
5791
5792 /* Only copy these flags into the `event_state`. */
5793 const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT;
5794
5795 wm_event_prev_values_set(event, event_state);
5796
5797 /* Copy to event state. */
5798 event_state->val = event->val;
5799 event_state->type = event->type;
5800 /* It's important only to write into the `event_state` modifier for keyboard
5801 * events because emulate MMB clears one of the modifiers in `event->modifier`,
5802 * making the second press not behave as if the modifier is pressed, see #96279. */
5803 if (is_keyboard) {
5804 event_state->modifier = event->modifier;
5805 }
5806 event_state->flag = (event->flag & event_state_flag_mask);
5807 /* NOTE: It's important that `keymodifier` is handled in the keyboard event handling logic
5808 * since the `event_state` and the `event` are not kept in sync. */
5809
5810 /* Double click test. */
5811 if (check_double_click &&
5812 wm_event_is_double_click(event, event_time_ms, *event_state_prev_press_time_ms_p))
5813 {
5814 CLOG_INFO(WM_LOG_HANDLERS, 1, "DBL_CLICK: detected");
5815 event->val = KM_DBL_CLICK;
5816 }
5817 else if (event->val == KM_PRESS) {
5818 if ((event->flag & WM_EVENT_IS_REPEAT) == 0) {
5819 wm_event_prev_click_set(event_time_ms, event_state, event_state_prev_press_time_ms_p);
5820 }
5821 }
5822}
5823
5825 uint64_t event_time_ms,
5826 wmEvent *event_state,
5827 uint64_t *event_state_prev_press_time_ms_p,
5828 const GHOST_TEventType type)
5829{
5830 const bool is_keyboard = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventKeyUp);
5831 const bool check_double_click = true;
5833 event_time_ms,
5834 event_state,
5835 event_state_prev_press_time_ms_p,
5836 is_keyboard,
5837 check_double_click);
5838}
5839
5840/* Returns true when the two events corresponds to a press of the same key with the same modifiers.
5841 */
5842static bool wm_event_is_same_key_press(const wmEvent &event_a, const wmEvent &event_b)
5843{
5844 if (event_a.val != KM_PRESS || event_b.val != KM_PRESS) {
5845 return false;
5846 }
5847
5848 if (event_a.modifier != event_b.modifier || event_a.type != event_b.type) {
5849 return false;
5850 }
5851
5852 return true;
5853}
5854
5863static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent &event)
5864{
5865 if (BLI_listbase_is_empty(&win->runtime->event_queue)) {
5866 /* If the queue is empty never ignore the event.
5867 * Empty queue at this point means that the events are handled fast enough, and there is no
5868 * reason to ignore anything. */
5869 return false;
5870 }
5871
5872 if ((event.flag & WM_EVENT_IS_REPEAT) == 0) {
5873 /* Only ignore repeat events from the keyboard, and allow accumulation of non-repeat events.
5874 *
5875 * The goal of this check is to allow events coming from a keyboard macro software, which can
5876 * generate events quicker than the main loop handles them. In this case we want all events to
5877 * be handled (unless the keyboard macro software tags them as repeat) because otherwise it
5878 * will become impossible to get reliable results of automated events testing. */
5879 return false;
5880 }
5881
5882 const wmEvent &last_event = *static_cast<const wmEvent *>(win->runtime->event_queue.last);
5883
5884 return wm_event_is_same_key_press(last_event, event);
5885}
5886
5888 wmWindow *win,
5889 const int type,
5890 const void *customdata,
5891 const uint64_t event_time_ms)
5892{
5893 if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
5894 return;
5895 }
5896
5907 wmEvent event, *event_state = win->eventstate;
5908 uint64_t *event_state_prev_press_time_ms_p = &win->eventstate_prev_press_time_ms;
5909
5910 /* Initialize and copy state (only mouse x y and modifiers). */
5911 event = *event_state;
5912 event.flag = (eWM_EventFlag)0;
5913
5924 event.prev_type = event.type;
5925 event.prev_val = event.val;
5926
5927 /* Always use modifiers from the active window since
5928 * changes to modifiers aren't sent to inactive windows, see: #66088. */
5929 if ((wm->winactive != win) && (wm->winactive && wm->winactive->eventstate)) {
5930 event.modifier = wm->winactive->eventstate->modifier;
5931 event.keymodifier = wm->winactive->eventstate->keymodifier;
5932 }
5933
5934 /* Ensure the event state is correct, any deviation from this may cause bugs.
5935 *
5936 * NOTE: #EVENT_NONE is set when unknown keys are pressed,
5937 * while not common, avoid a false alarm. */
5938#ifndef NDEBUG
5939 if ((event_state->type || event_state->val) && /* Ignore cleared event state. */
5940 !(ISKEYBOARD_OR_BUTTON(event_state->type) || (event_state->type == EVENT_NONE)))
5941 {
5943 "Non-keyboard/mouse button found in 'win->eventstate->type = %d'",
5944 event_state->type);
5945 }
5946 if ((event_state->prev_type || event_state->prev_val) && /* Ignore cleared event state. */
5947 !(ISKEYBOARD_OR_BUTTON(event_state->prev_type) || (event_state->type == EVENT_NONE)))
5948 {
5950 "Non-keyboard/mouse button found in 'win->eventstate->prev_type = %d'",
5951 event_state->prev_type);
5952 }
5953#endif
5954
5955 switch (type) {
5956 /* Mouse move, also to inactive window (X11 does this). */
5958 const GHOST_TEventCursorData *cd = static_cast<const GHOST_TEventCursorData *>(customdata);
5959
5960 copy_v2_v2_int(event.xy, &cd->x);
5961 wm_cursor_position_from_ghost_screen_coords(win, &event.xy[0], &event.xy[1]);
5962
5965
5966 event.type = MOUSEMOVE;
5967 event.val = KM_NOTHING;
5968 {
5969 wmEvent *event_new = wm_event_add_mousemove(win, &event);
5970 copy_v2_v2_int(event_state->xy, event_new->xy);
5971 event_state->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute;
5972 event_state->tablet.tilt = event.tablet.tilt;
5973 }
5974
5975 /* Also add to other window if event is there, this makes overdraws disappear nicely. */
5976 /* It remaps mouse-coord to other window in event. */
5977 wmWindow *win_other = wm_event_cursor_other_windows(wm, win, &event);
5978 if (win_other) {
5979 wmEvent event_other = *win_other->eventstate;
5980
5981 /* Use the modifier state of this window. */
5982 event_other.modifier = event.modifier;
5983 event_other.keymodifier = event.keymodifier;
5984
5985 /* See comment for this operation on `event` for details. */
5986 event_other.prev_type = event_other.type;
5987 event_other.prev_val = event_other.val;
5988
5989 copy_v2_v2_int(event_other.xy, event.xy);
5990 event_other.type = MOUSEMOVE;
5991 event_other.val = KM_NOTHING;
5992 {
5993 wmEvent *event_new = wm_event_add_mousemove(win_other, &event_other);
5994 copy_v2_v2_int(win_other->eventstate->xy, event_new->xy);
5996 }
5997 }
5998
5999 break;
6000 }
6001 case GHOST_kEventTrackpad: {
6002 const GHOST_TEventTrackpadData *pd = static_cast<const GHOST_TEventTrackpadData *>(
6003 customdata);
6004
6005 int delta[2] = {pd->deltaX, -pd->deltaY};
6006 switch (pd->subtype) {
6008 event.type = MOUSEZOOM;
6009 delta[0] = -delta[0];
6010 delta[1] = -delta[1];
6011 break;
6013 event.type = MOUSESMARTZOOM;
6014 break;
6016 event.type = MOUSEROTATE;
6017 break;
6019 default:
6020 event.type = MOUSEPAN;
6021 break;
6022 }
6023
6024 copy_v2_v2_int(event.xy, &pd->x);
6025 wm_cursor_position_from_ghost_screen_coords(win, &event.xy[0], &event.xy[1]);
6026 copy_v2_v2_int(event_state->xy, event.xy);
6027 event.val = KM_NOTHING;
6028
6029 /* The direction is inverted from the device due to system preferences. */
6030 if (pd->isDirectionInverted) {
6031 event.flag |= WM_EVENT_SCROLL_INVERT;
6032 }
6033
6034#if !defined(WIN32) && !defined(__APPLE__)
6035 /* Ensure "auto" is used when supported. */
6036 char trackpad_scroll_direction = U.trackpad_scroll_direction;
6038 switch (eUserpref_TrackpadScrollDir(trackpad_scroll_direction)) {
6040 event.flag &= ~WM_EVENT_SCROLL_INVERT;
6041 break;
6042 }
6044 event.flag |= WM_EVENT_SCROLL_INVERT;
6045 break;
6046 }
6047 }
6048 }
6049#endif
6050
6051 wm_event_add_trackpad(win, &event, delta[0], delta[1]);
6052 break;
6053 }
6054 /* Mouse button. */
6056 case GHOST_kEventButtonUp: {
6057 const GHOST_TEventButtonData *bd = static_cast<const GHOST_TEventButtonData *>(customdata);
6058
6059 /* Get value and type from GHOST.
6060 *
6061 * NOTE(@ideasman42): Unknown mouse buttons are treated as middle-mouse (historic stuff).
6062 * GHOST should never generate unknown events and this logic can probably be removed. */
6063 event.val = (type == GHOST_kEventButtonDown) ? KM_PRESS : KM_RELEASE;
6065
6066 /* Get tablet data. */
6068
6069 wm_eventemulation(&event, false);
6071 event_time_ms,
6072 event_state,
6073 event_state_prev_press_time_ms_p,
6074 (GHOST_TEventType)type);
6075
6076 /* Add to other window if event is there (not to both!). */
6077 wmWindow *win_other = wm_event_cursor_other_windows(wm, win, &event);
6078 if (win_other) {
6079 wmEvent event_other = *win_other->eventstate;
6080
6081 /* Use the modifier state of this window. */
6082 event_other.modifier = event.modifier;
6083 event_other.keymodifier = event.keymodifier;
6084
6085 /* See comment for this operation on `event` for details. */
6086 event_other.prev_type = event_other.type;
6087 event_other.prev_val = event_other.val;
6088
6089 copy_v2_v2_int(event_other.xy, event.xy);
6090
6091 event_other.type = event.type;
6092 event_other.val = event.val;
6093 event_other.tablet = event.tablet;
6094
6095 wm_event_add_intern(win_other, &event_other);
6096 }
6097 else {
6098 wm_event_add_intern(win, &event);
6099 }
6100
6101 break;
6102 }
6103 /* Keyboard. */
6105 case GHOST_kEventKeyUp: {
6106 const GHOST_TEventKeyData *kd = static_cast<const GHOST_TEventKeyData *>(customdata);
6107 event.type = wm_event_type_from_ghost_key(kd->key);
6108 if (UNLIKELY(event.type == EVENT_NONE)) {
6109 break;
6110 }
6111
6112 /* Might be not null terminated. */
6113 memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf));
6114 if (kd->is_repeat) {
6115 event.flag |= WM_EVENT_IS_REPEAT;
6116 }
6117 event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE;
6118
6119 wm_eventemulation(&event, false);
6120
6121 /* Exclude arrow keys, escape, etc from text input. */
6122 if (type == GHOST_kEventKeyUp) {
6123 /* Ghost should do this already for key up. */
6124 if (event.utf8_buf[0]) {
6126 "ghost on your platform is misbehaving, utf8 events on key up!");
6127 }
6128 event.utf8_buf[0] = '\0';
6129 }
6130 else {
6131 /* Check for ASCII control characters.
6132 * Inline `iscntrl` because the users locale must not change behavior. */
6133 if ((event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) || (event.utf8_buf[0] == 127)) {
6134 event.utf8_buf[0] = '\0';
6135 }
6136 }
6137
6138 if (event.utf8_buf[0]) {
6139 /* NOTE(@ideasman42): Detect non-ASCII characters stored in `utf8_buf`,
6140 * ideally this would never happen but it can't be ruled out for X11 which has
6141 * special handling of Latin1 when building without UTF8 support.
6142 * Avoid regressions by adding this conversions, it should eventually be removed. */
6143 if ((event.utf8_buf[0] >= 0x80) && (event.utf8_buf[1] == '\0')) {
6144 const uint c = uint(event.utf8_buf[0]);
6145 int utf8_buf_len = BLI_str_utf8_from_unicode(c, event.utf8_buf, sizeof(event.utf8_buf));
6147 "ghost detected non-ASCII single byte character '%u', converting to utf8 "
6148 "('%.*s', length=%d)",
6149 c,
6150 utf8_buf_len,
6151 event.utf8_buf,
6152 utf8_buf_len);
6153 }
6154
6155 const int utf8_buf_len = BLI_str_utf8_size_or_error(event.utf8_buf);
6156 if (utf8_buf_len == -1) {
6158 "ghost detected an invalid unicode character '%d'",
6159 int(uchar(event.utf8_buf[0])));
6160 event.utf8_buf[0] = '\0';
6161 }
6162 else {
6163#ifndef NDEBUG
6164 /* Ensure callers don't accidentally treat this as a "string",
6165 * it's not null terminated. */
6166 if (utf8_buf_len > 0) {
6167 for (int i = utf8_buf_len; i < ARRAY_SIZE(event.utf8_buf); i++) {
6168 event.utf8_buf[i] = 0xff;
6169 }
6170 }
6171#endif /* !NDEBUG */
6172 }
6173 }
6174
6175 /* NOTE(@ideasman42): Setting the modifier state based on press/release
6176 * is technically incorrect.
6177 *
6178 * - The user might hold both left/right modifier keys, then only release one.
6179 *
6180 * This could be solved by storing a separate flag for the left/right modifiers,
6181 * and combine them into `event.modifiers`.
6182 *
6183 * - The user might have multiple keyboards (or keyboard + NDOF device)
6184 * where it's possible to press the same modifier key multiple times.
6185 *
6186 * This could be solved by tracking the number of held modifier keys,
6187 * (this is in fact what LIBXKB does), however doing this relies on all GHOST
6188 * back-ends properly reporting every press/release as any mismatch could result
6189 * in modifier keys being stuck (which is very bad!).
6190 *
6191 * To my knowledge users never reported a bug relating to these limitations so
6192 * it seems reasonable to keep the current logic. */
6193
6194 switch (event.type) {
6195 case EVT_LEFTSHIFTKEY:
6196 case EVT_RIGHTSHIFTKEY: {
6197 SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_SHIFT);
6198 break;
6199 }
6200 case EVT_LEFTCTRLKEY:
6201 case EVT_RIGHTCTRLKEY: {
6202 SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_CTRL);
6203 break;
6204 }
6205 case EVT_LEFTALTKEY:
6206 case EVT_RIGHTALTKEY: {
6207 SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_ALT);
6208 break;
6209 }
6210 case EVT_OSKEY: {
6211 SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_OSKEY);
6212 break;
6213 }
6214 case EVT_HYPER: {
6215 SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_HYPER);
6216 break;
6217 }
6218 default: {
6219 if (event.val == KM_PRESS) {
6220 if (event.keymodifier == 0) {
6221 /* Only set in `eventstate`, for next event. */
6222 event_state->keymodifier = event.type;
6223 }
6224 }
6225 else {
6226 BLI_assert(event.val == KM_RELEASE);
6227 if (event.keymodifier == event.type) {
6228 event.keymodifier = event_state->keymodifier = EVENT_NONE;
6229 }
6230 }
6231
6232 /* This case happens on holding a key pressed,
6233 * it should not generate press events with the same key as modifier. */
6234 if (event.keymodifier == event.type) {
6235 event.keymodifier = EVENT_NONE;
6236 }
6237 else if (event.keymodifier == EVT_UNKNOWNKEY) {
6238 /* This case happens with an external number-pad, and also when using 'dead keys'
6239 * (to compose complex Latin characters e.g.), it's not really clear why.
6240 * Since it's impossible to map a key modifier to an unknown key,
6241 * it shouldn't harm to clear it. */
6242 event_state->keymodifier = event.keymodifier = EVENT_NONE;
6243 }
6244 break;
6245 }
6246 }
6247
6248 /* It's important `event.modifier` has been initialized first. */
6250 event_time_ms,
6251 event_state,
6252 event_state_prev_press_time_ms_p,
6253 (GHOST_TEventType)type);
6254
6255 /* If test_break set, it catches this. Do not set with modifier presses.
6256 * Exclude modifiers because MS-Windows uses these to bring up the task manager.
6257 *
6258 * NOTE: in general handling events here isn't great design as
6259 * event handling should be managed by the event handling loop.
6260 * Make an exception for `G.is_break` as it ensures we can always cancel operations
6261 * such as rendering or baking no matter which operation is currently handling events. */
6262 if ((event.type == EVT_ESCKEY) && (event.val == KM_PRESS) && (event.modifier == 0)) {
6263 G.is_break = true;
6264 }
6265
6266 if (!wm_event_is_ignorable_key_press(win, event)) {
6267 wm_event_add_intern(win, &event);
6268 }
6269
6270 break;
6271 }
6272
6273 case GHOST_kEventWheel: {
6274 const GHOST_TEventWheelData *wheelData = static_cast<const GHOST_TEventWheelData *>(
6275 customdata);
6276
6277 int click_step;
6278 if (wheelData->axis == GHOST_kEventWheelAxisVertical) {
6279 if (wheelData->value > 0) {
6280 event.type = WHEELUPMOUSE;
6281 click_step = wheelData->value;
6282 }
6283 else {
6284 event.type = WHEELDOWNMOUSE;
6285 click_step = -wheelData->value;
6286 }
6287 }
6288 else {
6289 if (wheelData->value > 0) {
6290 event.type = WHEELRIGHTMOUSE;
6291 click_step = wheelData->value;
6292 }
6293 else {
6294 event.type = WHEELLEFTMOUSE;
6295 click_step = -wheelData->value;
6296 }
6297 }
6298 BLI_assert(click_step != 0);
6299
6300 /* Avoid generating a large number of events.
6301 * In practice this values is typically 1, sometimes 2-3, even 32 is very high
6302 * although this could happen if the system freezes. */
6303 click_step = std::min(click_step, 32);
6304
6305 /* TODO: support a wheel event that includes the number of steps
6306 * instead of generating multiple events. */
6307 event.val = KM_PRESS;
6308 for (int i = 0; i < click_step; i++) {
6309 wm_event_add_intern(win, &event);
6310 }
6311
6312 break;
6313 }
6314
6315#ifdef WITH_INPUT_NDOF
6316 case GHOST_kEventNDOFMotion: {
6317 event.type = NDOF_MOTION;
6318 event.val = KM_NOTHING;
6319 attach_ndof_data(&event, static_cast<const GHOST_TEventNDOFMotionData *>(customdata));
6320 wm_event_add_intern(win, &event);
6321
6322 CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.xy[0], event.xy[1]);
6323 break;
6324 }
6325
6326 case GHOST_kEventNDOFButton: {
6327 const GHOST_TEventNDOFButtonData *e = static_cast<const GHOST_TEventNDOFButtonData *>(
6328 customdata);
6329 event.type = wm_event_type_from_ndof_button(static_cast<GHOST_NDOF_ButtonT>(e->button));
6330
6331 switch (e->action) {
6332 case GHOST_kPress:
6333 event.val = KM_PRESS;
6334 break;
6335 case GHOST_kRelease:
6336 event.val = KM_RELEASE;
6337 break;
6338 default:
6340 }
6341
6342 event.custom = 0;
6343 event.customdata = nullptr;
6344
6346 event_time_ms,
6347 event_state,
6348 event_state_prev_press_time_ms_p,
6349 (GHOST_TEventType)type);
6350
6351 wm_event_add_intern(win, &event);
6352
6353 break;
6354 }
6355#endif /* WITH_INPUT_NDOF */
6356
6359 break;
6360
6362 event.type = WINDEACTIVATE;
6363 wm_event_add_intern(win, &event);
6364
6365 break;
6366 }
6367
6368#ifdef WITH_INPUT_IME
6370 event.val = KM_PRESS;
6371 BLI_assert(customdata != nullptr);
6372 /* We need to free the previously allocated data (if any). */
6373 MEM_delete(win->runtime->ime_data);
6374
6375 /* We make a copy of the ghost custom data as it is not certain that the pointer
6376 * will be valid after the event itself gets freed.
6377 */
6378 const wmIMEData *ghost_event_data = static_cast<const wmIMEData *>(customdata);
6379 win->runtime->ime_data = MEM_new<wmIMEData>(__func__, *ghost_event_data);
6380
6381 win->runtime->ime_data_is_composing = true;
6382 event.type = WM_IME_COMPOSITE_START;
6383 wm_event_add_intern(win, &event);
6384 break;
6385 }
6387 event.val = KM_PRESS;
6388 event.type = WM_IME_COMPOSITE_EVENT;
6389
6390 /* Update our copy of the ghost custom data. */
6391 MEM_delete(win->runtime->ime_data);
6392 const wmIMEData *ghost_event_data = static_cast<const wmIMEData *>(customdata);
6393 win->runtime->ime_data = MEM_new<wmIMEData>(__func__, *ghost_event_data);
6394
6395 wm_event_add_intern(win, &event);
6396 break;
6397 }
6399 event.val = KM_PRESS;
6400 win->runtime->ime_data_is_composing = false;
6401 event.type = WM_IME_COMPOSITE_END;
6402 wm_event_add_intern(win, &event);
6403 break;
6404 }
6405#endif /* WITH_INPUT_IME */
6406 }
6407
6408#if 0
6409 WM_event_print(&event);
6410#endif
6411}
6412
6413#ifdef WITH_XR_OPENXR
6414void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val)
6415{
6417
6418 wmEvent event{};
6419 event.type = EVT_XR_ACTION;
6420 event.val = val;
6421 event.flag = (eWM_EventFlag)0;
6422 event.custom = EVT_DATA_XR;
6423 event.customdata = actiondata;
6424 event.customdata_free = true;
6425
6426 WM_event_add(win, &event);
6427}
6428#endif /* WITH_XR_OPENXR */
6429
6431
6432/* -------------------------------------------------------------------- */
6435
6441{
6443
6444 if (wm->runtime->is_interface_locked) {
6445 if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) {
6446 return false;
6447 }
6448 }
6449
6450 return true;
6451}
6452
6454{
6455 /* This will prevent events from being handled while interface is locked
6456 *
6457 * Use a "local" flag for now, because currently no other areas could
6458 * benefit of locked interface anyway (aka using G.is_interface_locked
6459 * wouldn't be useful anywhere outside of window manager, so let's not
6460 * pollute global context with such an information for now).
6461 */
6462 wm->runtime->is_interface_locked = (lock_flags != 0);
6463
6464 BKE_spacedata_draw_locks(static_cast<ARegionDrawLockFlags>(lock_flags));
6465}
6466
6471
6473
6474/* -------------------------------------------------------------------- */
6477
6479 wmWindow *win,
6480 wmEventHandler_Keymap *handler,
6481 wmEventHandler_KeymapResult *km_result)
6482{
6483 if (handler->dynamic.keymap_fn != nullptr) {
6484 handler->dynamic.keymap_fn(wm, win, handler, km_result);
6485 BLI_assert(handler->keymap == nullptr);
6486 }
6487 else {
6488 memset(km_result, 0x0, sizeof(*km_result));
6489 wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap);
6490 BLI_assert(keymap != nullptr);
6491 if (keymap != nullptr) {
6492 km_result->keymaps[km_result->keymaps_len++] = keymap;
6493 }
6494 }
6495}
6496
6498{
6499 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
6500 if (wm_eventmatch(event, kmi)) {
6501 wmOperatorType *ot = WM_operatortype_find(kmi->idname, false);
6503 return kmi;
6504 }
6505 }
6506 }
6507 return nullptr;
6508}
6509
6511 bContext *C, wmWindowManager *wm, wmWindow *win, ListBase *handlers, const wmEvent *event)
6512{
6513 LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
6514 /* During this loop, UI handlers for nested menus can tag multiple handlers free. */
6515 if (handler_base->flag & WM_HANDLER_DO_FREE) {
6516 /* Pass. */
6517 }
6518 else if (handler_base->poll == nullptr ||
6519 handler_base->poll(win, CTX_wm_area(C), CTX_wm_region(C), event))
6520 {
6521 if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) {
6522 wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
6524 WM_event_get_keymaps_from_handler(wm, win, handler, &km_result);
6525 for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) {
6526 wmKeyMap *keymap = km_result.keymaps[km_index];
6527 if (WM_keymap_poll(C, keymap)) {
6528 wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event);
6529 if (kmi != nullptr) {
6530 return kmi;
6531 }
6532 }
6533 }
6534 }
6535 }
6536 }
6537 return nullptr;
6538}
6539
6540bool WM_event_match(const wmEvent *winevent, const wmKeyMapItem *kmi)
6541{
6542 return wm_eventmatch(winevent, kmi);
6543}
6544
6546
6547/* -------------------------------------------------------------------- */
6554
6563
6574
6576 const wmEvent *event,
6577 short space_type,
6578 short region_type,
6579 const bToolRef *tref)
6580{
6581 state->modifier = event->modifier;
6582 state->space_type = space_type;
6583 state->region_type = region_type;
6584 state->tref = tref ? *tref : bToolRef{};
6585}
6586
6588 int button_index,
6589 int type_index)
6590{
6591 if (win->cursor_keymap_status != nullptr) {
6592 CursorKeymapInfo *cd = static_cast<CursorKeymapInfo *>(win->cursor_keymap_status);
6593 const char *msg = cd->text[button_index][type_index];
6594 if (*msg) {
6595 return msg;
6596 }
6597 }
6598 return nullptr;
6599}
6600
6602{
6603 if (screen->state == SCREENFULL) {
6604 return nullptr;
6605 }
6606 ScrArea *area_statusbar = nullptr;
6608 if (area->spacetype == SPACE_STATUSBAR) {
6609 area_statusbar = area;
6610 break;
6611 }
6612 }
6613 return area_statusbar;
6614}
6615
6617{
6618 bScreen *screen = WM_window_get_active_screen(win);
6619 ScrArea *area = WM_window_status_area_find(win, screen);
6620 if (area != nullptr) {
6621 ED_area_tag_redraw(area);
6622 }
6623}
6624
6626{
6627 bScreen *screen = WM_window_get_active_screen(win);
6628 ScrArea *area_statusbar = WM_window_status_area_find(win, screen);
6629 if (area_statusbar == nullptr) {
6631 return;
6632 }
6633
6634 CursorKeymapInfo *cd;
6635 if (UNLIKELY(win->cursor_keymap_status == nullptr)) {
6637 }
6638 cd = static_cast<CursorKeymapInfo *>(win->cursor_keymap_status);
6639
6640 /* Detect unchanged state (early exit). */
6641 if (memcmp(&cd->state_event, win->eventstate, sizeof(wmEvent)) == 0) {
6642 return;
6643 }
6644
6645 /* Now perform more comprehensive check,
6646 * still keep this fast since it happens on mouse-move. */
6648 cd->state_event = *win->eventstate;
6649
6650 /* Find active region and associated area. */
6651 ARegion *region = screen->active_region;
6652 if (region == nullptr) {
6653 return;
6654 }
6655
6656 ScrArea *area = nullptr;
6657 ED_screen_areas_iter (win, screen, area_iter) {
6658 if (BLI_findindex(&area_iter->regionbase, region) != -1) {
6659 area = area_iter;
6660 break;
6661 }
6662 }
6663 if (area == nullptr) {
6664 return;
6665 }
6666
6667 /* Keep as-is. */
6669 return;
6670 }
6671 if (ELEM(region->regiontype,
6677 RGN_TYPE_HUD))
6678 {
6679 return;
6680 }
6681 /* Fall back to window. */
6684 }
6685
6686 /* Detect changes to the state. */
6687 {
6688 bToolRef *tref = nullptr;
6689 if ((region->regiontype == RGN_TYPE_WINDOW) &&
6690 ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK))
6691 {
6692 const Scene *scene = WM_window_get_active_scene(win);
6693 ViewLayer *view_layer = WM_window_get_active_view_layer(win);
6694 WorkSpace *workspace = WM_window_get_active_workspace(win);
6695 bToolKey tkey{};
6696 tkey.space_type = area->spacetype;
6697 tkey.mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype);
6698 tref = WM_toolsystem_ref_find(workspace, &tkey);
6699 }
6700 wm_event_cursor_store(&cd->state, win->eventstate, area->spacetype, region->regiontype, tref);
6701 if (memcmp(&cd->state, &cd_prev.state, sizeof(cd->state)) == 0) {
6702 return;
6703 }
6704 }
6705
6706 /* Changed context found, detect changes to key-map and refresh the status bar. */
6707 const struct {
6708 int button_index;
6709 int type_index; /* 0: press or click, 1: drag. */
6710 wmEventType event_type;
6711 int event_value;
6712 } event_data[] = {
6713 {0, 0, LEFTMOUSE, KM_PRESS},
6714 {0, 0, LEFTMOUSE, KM_CLICK},
6715 {0, 0, LEFTMOUSE, KM_CLICK_DRAG},
6716
6717 {1, 0, MIDDLEMOUSE, KM_PRESS},
6718 {1, 0, MIDDLEMOUSE, KM_CLICK},
6719 {1, 0, MIDDLEMOUSE, KM_CLICK_DRAG},
6720
6721 {2, 0, RIGHTMOUSE, KM_PRESS},
6722 {2, 0, RIGHTMOUSE, KM_CLICK},
6723 {2, 0, RIGHTMOUSE, KM_CLICK_DRAG},
6724 };
6725
6726 for (int button_index = 0; button_index < 3; button_index++) {
6727 cd->text[button_index][0][0] = '\0';
6728 cd->text[button_index][1][0] = '\0';
6729 }
6730
6731 CTX_wm_window_set(C, win);
6732 CTX_wm_area_set(C, area);
6733 CTX_wm_region_set(C, region);
6734
6735 ListBase *handlers[] = {
6736 &region->runtime->handlers,
6737 &area->handlers,
6738 &win->handlers,
6739 };
6740
6742 for (int data_index = 0; data_index < ARRAY_SIZE(event_data); data_index++) {
6743 const int button_index = event_data[data_index].button_index;
6744 const int type_index = event_data[data_index].type_index;
6745 if (cd->text[button_index][type_index][0] != 0) {
6746 continue;
6747 }
6748 wmEvent test_event = *win->eventstate;
6749 test_event.type = event_data[data_index].event_type;
6750 test_event.val = event_data[data_index].event_value;
6751 test_event.flag = (eWM_EventFlag)0;
6752 wm_eventemulation(&test_event, true);
6753 wmKeyMapItem *kmi = nullptr;
6754 for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) {
6756 C, wm, win, handlers[handler_index], &test_event);
6757 if (kmi) {
6758 break;
6759 }
6760 }
6761 if (kmi) {
6763 std::string name;
6764
6765 if (kmi->type == RIGHTMOUSE && kmi->val == KM_PRESS &&
6766 STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie", "WM_OT_call_panel"))
6767 {
6768 name = IFACE_("Options");
6769 }
6770 else if (ot) {
6771 /* Skip internal operators. */
6772 if (ot->flag & OPTYPE_INTERNAL) {
6773 continue;
6774 }
6775 name = WM_operatortype_name(ot, kmi->ptr);
6776 }
6777 else {
6778 name = kmi->idname;
6779 }
6780
6781 STRNCPY(cd->text[button_index][type_index], name.c_str());
6782 }
6783 }
6784
6785 if (memcmp(&cd_prev.text, &cd->text, sizeof(cd_prev.text)) != 0) {
6786 ED_area_tag_redraw(area_statusbar);
6787 }
6788
6789 CTX_wm_window_set(C, nullptr);
6790}
6791
6793
6794/* -------------------------------------------------------------------- */
6797
6799{
6801 wmKeyMap *keymap = nullptr;
6802 wmOperator *op = nullptr;
6803 LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {
6804 if (handler_base->type == WM_HANDLER_TYPE_OP) {
6805 wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base;
6806 if (handler->op != nullptr) {
6807 /* 'handler->keymap' could be checked too, seems not to be used. */
6808 wmOperator *op_test = handler->op->opm ? handler->op->opm : handler->op;
6809 wmKeyMap *keymap_test = WM_keymap_active(wm, op_test->type->modalkeymap);
6810 if (keymap_test && keymap_test->modal_items) {
6811 keymap = keymap_test;
6812 op = op_test;
6813 break;
6814 }
6815 }
6816 }
6817 }
6818 if (keymap == nullptr || keymap->modal_items == nullptr) {
6819 return false;
6820 }
6821 const EnumPropertyItem *items = static_cast<const EnumPropertyItem *>(keymap->modal_items);
6822
6823 uiLayout *row = &layout->row(true);
6824 for (int i = 0; items[i].identifier; i++) {
6825 if (!items[i].identifier[0]) {
6826 continue;
6827 }
6828 if ((keymap->poll_modal_item != nullptr) &&
6829 (keymap->poll_modal_item(op, items[i].value) == false))
6830 {
6831 continue;
6832 }
6833
6834 const int num_items_used = uiTemplateStatusBarModalItem(row, keymap, items + i);
6835 if (num_items_used > 0) {
6836 /* Skip items in case consecutive items were merged. */
6837 i += num_items_used - 1;
6838 }
6839 else if (std::optional<std::string> str = WM_modalkeymap_operator_items_to_string(
6840 op->type, items[i].value, true))
6841 {
6842 /* Show text instead */
6843 row->label(fmt::format("{}: {}", *str, items[i].name), ICON_NONE);
6844 }
6845 }
6846 return true;
6847}
6848
void AS_asset_library_remap_ids(const blender::bke::id::IDRemapper &mappings)
void CTX_wm_gizmo_group_set(bContext *C, wmGizmoGroup *gzgroup)
ReportList * CTX_wm_reports(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
ARegion * CTX_wm_region_popup(const bContext *C)
void CTX_wm_operator_poll_msg_clear(bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
const bContextStore * CTX_store_get(const bContext *C)
void CTX_store_set(bContext *C, const bContextStore *store)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst, const CustomData_MeshMasks *mask_src)
Definition customdata.cc:94
@ G_FLAG_EVENT_SIMULATE
#define G_MAIN
@ G_DEBUG_HANDLERS
@ G_DEBUG_WM
@ G_DEBUG_EVENTS
char * IDP_reprN(const IDProperty *prop, uint *r_len)
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1243
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:873
void IDP_ReplaceGroupInGroup(IDProperty *dest, const IDProperty *src) ATTR_NONNULL()
Definition idprop.cc:630
void BKE_reports_free(ReportList *reports)
Definition report.cc:70
void BKE_report_print_level_set(ReportList *reports, eReportType level)
Definition report.cc:238
void BKE_reports_clear(ReportList *reports)
Definition report.cc:82
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void BKE_reports_init(ReportList *reports, int flag)
Definition report.cc:55
void BKE_reports_print(ReportList *reports, eReportType level)
Definition report.cc:316
void BKE_reports_move_to_reports(ReportList *reports_dst, ReportList *reports_src)
Definition report.cc:114
Depsgraph * BKE_scene_ensure_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer)
Definition scene.cc:3427
void BKE_scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain)
Definition scene.cc:2618
ARegion * BKE_area_find_region_xy(const ScrArea *area, int regiontype, const int xy[2]) ATTR_NONNULL(3)
Definition screen.cc:869
ARegion * BKE_area_find_region_active_win(const ScrArea *area)
Definition screen.cc:853
void BKE_spacedata_draw_locks(ARegionDrawLockFlags lock_flags)
Definition screen.cc:417
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
ARegionDrawLockFlags
@ REGION_DRAW_LOCK_ALL
bScreen * BKE_workspace_layout_screen_get(const WorkSpaceLayout *layout) GETTER_ATTRS
Definition workspace.cc:637
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_INLINE
bool BLI_gset_ensure_p_ex(GSet *gs, const void *key, void ***r_key)
Definition BLI_ghash.cc:971
unsigned int BLI_ghashutil_ptrhash(const void *key)
GSet * BLI_gset_new_ex(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:936
bool BLI_gset_remove(GSet *gs, const void *key, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.cc:999
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
void * BLI_poptail(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:261
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
bool BLI_remlink_safe(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:154
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE int len_manhattan_v2v2_int(const int a[2], const int b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.cc:566
#define STR_ELEM(...)
Definition BLI_string.h:656
char char * BLI_vsprintfN(const char *__restrict format, va_list args) ATTR_NONNULL(1
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
size_t BLI_str_utf8_from_unicode(unsigned int c, char *dst, size_t dst_maxncpy) ATTR_NONNULL(2)
const char int BLI_str_utf8_size_or_error(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
unsigned char uchar
unsigned int uint
void BLI_timer_execute(void)
Definition BLI_timer.cc:120
#define UNPACK2(a)
#define ARRAY_SIZE(arr)
#define ENUM_OPERATORS(_type, _max)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_STR_INFO(clg_ref, level, str)
Definition CLG_log.h:185
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
void DEG_graph_tag_on_visible_update(Depsgraph *depsgraph, bool do_time)
void DEG_make_active(Depsgraph *depsgraph)
Definition depsgraph.cc:336
@ SACTION_SHOW_MARKERS
These structs are the foundation for all linked lists in the library system.
@ SCE_WORKSPACE_TOOL_FALLBACK
@ RGN_FLAG_HIDDEN
@ SCREENFULL
@ RGN_TYPE_CHANNELS
@ RGN_TYPE_TOOL_HEADER
@ RGN_TYPE_TEMPORARY
@ RGN_TYPE_ASSET_SHELF_HEADER
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HUD
@ RGN_TYPE_PREVIEW
@ RGN_TYPE_FOOTER
@ RGN_TYPE_HEADER
@ RGN_TYPE_TOOLS
@ RGN_TYPE_TOOL_PROPS
#define RGN_TYPE_ANY
@ RGN_ALIGN_BOTTOM
#define RGN_TYPE_IS_HEADER_ANY(regiontype)
@ AREA_FLAG_OFFSCREEN
@ AREA_FLAG_ACTIONZONES_UPDATE
@ SPACE_ACTION
@ SPACE_STATUSBAR
@ SPACE_TOPBAR
@ SPACE_FILE
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SIPO_SHOW_MARKERS
@ SNLA_SHOW_MARKERS
@ SEQ_SHOW_MARKERS
#define UI_SCALE_FAC
@ USER_CONTINUOUS_MOUSE
@ USER_EMU_MMB_MOD_OSKEY
@ USER_TOOLTIPS
@ USER_NONUMPAD
@ USER_TWOBUTTONMOUSE
eUserpref_TrackpadScrollDir
@ USER_TRACKPAD_SCROLL_DIR_TRADITIONAL
@ USER_TRACKPAD_SCROLL_DIR_NATURAL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ OP_IS_REPEAT_LAST
@ OP_IS_MODAL_CURSOR_REGION
@ OP_IS_MODAL_GRAB_CURSOR
#define OPERATOR_RETVAL_CHECK(ret)
@ TOOLREF_FLAG_FALLBACK_KEYMAP
ScrArea * ED_fileselect_handler_area_find(const wmWindow *win, const wmOperator *file_operator)
Definition filesel.cc:1455
ScrArea * ED_fileselect_handler_area_find_any_with_op(const wmWindow *win)
Definition filesel.cc:1472
void ED_fileselect_params_to_userdef(SpaceFile *sfile, const int temp_win_size[2], bool is_maximized)
Definition filesel.cc:691
void ED_fileselect_set_params_from_userdef(SpaceFile *sfile)
Definition filesel.cc:661
void ED_fileselect_window_params_get(const wmWindow *win, int r_win_size[2], bool *r_is_maximized)
Definition filesel.cc:633
void ED_info_stats_clear(wmWindowManager *wm, ViewLayer *view_layer)
void ED_preview_restart_queue_work(const bContext *C)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:714
ScrArea * ED_screen_temp_space_open(bContext *C, const char *title, const rcti *rect_unscaled, eSpace_Type space_type, int display_type, bool dialog) ATTR_NONNULL(1
void ED_region_do_listen(wmRegionListenerParams *params)
Definition area.cc:129
ARegion * ED_area_find_region_xy_visual(const ScrArea *area, int regiontype, const int event_xy[2])
#define ED_screen_areas_iter(win, screen, area_name)
Definition ED_screen.hh:288
void ED_area_status_text(ScrArea *area, const char *str)
Definition area.cc:872
void ED_workspace_do_listen(bContext *C, const wmNotifier *note)
void ED_area_do_listen(wmSpaceTypeListenerParams *params)
Definition area.cc:161
void ED_screen_do_listen(bContext *C, const wmNotifier *note)
bool ED_screen_change(bContext *C, bScreen *screen)
Change the active screen.
bool ED_workspace_layout_delete(WorkSpace *workspace, WorkSpaceLayout *layout_old, bContext *C) ATTR_NONNULL()
bool ED_workspace_delete(WorkSpace *workspace, Main *bmain, bContext *C, wmWindowManager *wm) ATTR_NONNULL()
void ED_area_type_hud_clear(wmWindowManager *wm, ScrArea *area_keep)
void ED_area_exit(bContext *C, ScrArea *area)
void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2])
void ED_area_do_refresh(bContext *C, ScrArea *area)
Definition area.cc:169
AZone * ED_area_azones_update(ScrArea *area, const int mouse_xy[2])
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
void ED_area_prevspace(bContext *C, ScrArea *area)
Definition area.cc:2860
void ED_screen_full_prevspace(bContext *C, ScrArea *area)
void ED_area_type_hud_ensure(bContext *C, ScrArea *area)
void ED_undo_push_op(bContext *C, wmOperator *op)
Definition ed_undo.cc:358
bool ED_undo_is_state_valid(bContext *C)
Definition ed_undo.cc:64
void ED_undo_grouped_push_op(bContext *C, wmOperator *op)
Definition ed_undo.cc:364
void ED_spacedata_id_remap(ScrArea *area, SpaceLink *sl, const blender::bke::id::IDRemapper &mappings)
Definition ed_util.cc:450
void ED_view3d_screen_datamask(const Scene *scene, ViewLayer *view_layer, const bScreen *screen, CustomData_MeshMasks *r_cddata_masks)
GHOST C-API function and type declarations.
@ GHOST_kEventWheelAxisVertical
@ GHOST_kTrackpadEventMagnify
@ GHOST_kTrackpadEventSmartMagnify
@ GHOST_kTrackpadEventRotate
@ GHOST_kTrackpadEventScroll
GHOST_NDOF_ButtonT
GHOST_TEventType
@ GHOST_kEventWheel
@ GHOST_kEventImeComposition
@ GHOST_kEventCursorMove
@ GHOST_kEventButtonUp
@ GHOST_kEventTrackpad
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventButtonDown
@ GHOST_kEventKeyDown
@ GHOST_kEventImeCompositionStart
@ GHOST_kEventImeCompositionEnd
@ GHOST_kEventUnknown
@ GHOST_kEventKeyUp
@ GHOST_kTabletModeNone
GHOST_TKey
@ GHOST_kKeyLeftOS
@ GHOST_kKeyInsert
@ GHOST_kKeySemicolon
@ GHOST_kKeyMediaPlay
@ GHOST_kKeyZ
@ GHOST_kKeyQuote
@ GHOST_kKeyAccentGrave
@ GHOST_kKeyLeftAlt
@ GHOST_kKeyRightShift
@ GHOST_kKeyNumLock
@ GHOST_kKeyEnter
@ GHOST_kKeyNumpadSlash
@ GHOST_kKeyRightArrow
@ GHOST_kKeyF24
@ GHOST_kKeyPause
@ GHOST_kKeyCapsLock
@ GHOST_kKeyApp
@ GHOST_kKeyMinus
@ GHOST_kKeyMediaStop
@ GHOST_kKeyBackSpace
@ GHOST_kKey0
@ GHOST_kKeyDownPage
@ GHOST_kKeyGrLess
@ GHOST_kKeyDownArrow
@ GHOST_kKeyRightOS
@ GHOST_kKeyClear
@ GHOST_kKeyNumpadPeriod
@ GHOST_kKeyF1
@ GHOST_kKeyNumpadAsterisk
@ GHOST_kKeyPrintScreen
@ GHOST_kKeyLeftControl
@ GHOST_kKeyLeftBracket
@ GHOST_kKeyTab
@ GHOST_kKeyComma
@ GHOST_kKeyRightBracket
@ GHOST_kKeyBackslash
@ GHOST_kKeyLinefeed
@ GHOST_kKeyLeftHyper
@ GHOST_kKeyRightAlt
@ GHOST_kKeyPeriod
@ GHOST_kKeyNumpadPlus
@ GHOST_kKeyUpPage
@ GHOST_kKey9
@ GHOST_kKeyLeftArrow
@ GHOST_kKeyEqual
@ GHOST_kKeyHome
@ GHOST_kKeyNumpad9
@ GHOST_kKeyEnd
@ GHOST_kKeyUpArrow
@ GHOST_kKeyDelete
@ GHOST_kKeyNumpad0
@ GHOST_kKeyA
@ GHOST_kKeyMediaFirst
@ GHOST_kKeyRightControl
@ GHOST_kKeyEsc
@ GHOST_kKeyPlus
@ GHOST_kKeyUnknown
@ GHOST_kKeyScrollLock
@ GHOST_kKeySlash
@ GHOST_kKeyNumpadEnter
@ GHOST_kKeyNumpadMinus
@ GHOST_kKeyRightHyper
@ GHOST_kKeyLeftShift
@ GHOST_kKeyMediaLast
@ GHOST_kKeySpace
#define GHOST_kNumEventTypes
GHOST_TButton
@ GHOST_kButtonMaskRight
@ GHOST_kButtonMaskButton4
@ GHOST_kButtonMaskNone
@ GHOST_kButtonMaskLeft
@ GHOST_kButtonMaskButton7
@ GHOST_kButtonMaskButton6
@ GHOST_kButtonMaskButton5
@ GHOST_kButtonMaskMiddle
void GPU_render_end()
void GPU_render_begin()
Read Guarded memory(de)allocation.
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
@ PROP_STRING
Definition RNA_types.hh:153
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_FILENAME
Definition RNA_types.hh:226
@ PROP_DIRPATH
Definition RNA_types.hh:225
@ PROP_FILEPATH
Definition RNA_types.hh:224
#define C
Definition RandGen.cpp:29
uiBlock * UI_region_block_find_mouse_over(const ARegion *region, const int xy[2], bool only_clip)
void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL()
int uiTemplateStatusBarModalItem(uiLayout *layout, const wmKeyMap *keymap, const EnumPropertyItem *item)
void UI_popup_handlers_remove_all(bContext *C, ListBase *handlers)
#define UI_MARKER_MARGIN_Y
Definition UI_view2d.hh:470
int(*)(bContext *C, const wmEvent *event, void *userdata) wmUIHandlerFunc
Definition WM_api.hh:641
eWM_EventHandlerFlag
Definition WM_api.hh:541
@ WM_HANDLER_BLOCKING
Definition WM_api.hh:543
@ WM_HANDLER_DO_FREE
Definition WM_api.hh:549
@ WM_HANDLER_ACCEPT_DBL_CLICK
Definition WM_api.hh:545
@ WM_CAPABILITY_WINDOW_POSITION
Definition WM_api.hh:182
@ WM_CAPABILITY_TRACKPAD_PHYSICAL_DIRECTION
Definition WM_api.hh:199
bool(*)(const wmWindow *win, const ScrArea *area, const ARegion *region, const wmEvent *event) EventHandlerPoll
Definition WM_api.hh:553
void(*)(wmWindowManager *wm, wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result) wmEventHandler_KeymapDynamicFn
Definition WM_api.hh:605
void(*)(bContext *C, void *userdata) wmUIHandlerRemoveFunc
Definition WM_api.hh:642
@ WM_GIZMO_HIDDEN_KEYMAP
@ WM_GIZMO_EVENT_HANDLE_ALL
@ WM_GIZMO_OPERATOR_TOOL_INIT
@ WM_GIZMO_NO_TOOLTIP
@ WM_GIZMOGROUPTYPE_TOOL_INIT
@ WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP
#define WM_TOOLSYSTEM_SPACE_MASK
#define ND_FILEREAD
Definition WM_types.hh:409
#define NC_GEOM
Definition WM_types.hh:390
#define WM_UI_HANDLER_CONTINUE
Definition WM_types.hh:345
#define ND_SPACE_INFO
Definition WM_types.hh:518
wmEventModifierFlag
Definition WM_types.hh:274
@ KM_CTRL
Definition WM_types.hh:276
@ KM_ALT
Definition WM_types.hh:277
@ KM_HYPER
Definition WM_types.hh:289
@ KM_OSKEY
Definition WM_types.hh:279
@ KM_SHIFT
Definition WM_types.hh:275
eWM_EventFlag
Definition WM_types.hh:668
@ WM_EVENT_FORCE_DRAG_THRESHOLD
Definition WM_types.hh:694
@ WM_EVENT_SCROLL_INVERT
Definition WM_types.hh:674
@ WM_EVENT_IS_CONSECUTIVE
Definition WM_types.hh:689
@ WM_EVENT_IS_REPEAT
Definition WM_types.hh:681
CLG_LogRef * WM_LOG_OPERATORS
#define NC_WM
Definition WM_types.hh:371
#define WM_OP_CONTEXT_HAS_AREA(type)
Definition WM_types.hh:253
CLG_LogRef * WM_LOG_HANDLERS
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_LOCK_BYPASS
Definition WM_types.hh:205
@ OPTYPE_MODAL_PRIORITY
Definition WM_types.hh:221
@ OPTYPE_UNDO_GROUPED
Definition WM_types.hh:207
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:188
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ OPTYPE_GRAB_CURSOR_X
Definition WM_types.hh:190
@ OPTYPE_GRAB_CURSOR_Y
Definition WM_types.hh:192
#define NOTE_ACTION
Definition WM_types.hh:580
#define ND_DATACHANGED
Definition WM_types.hh:411
CLG_LogRef * WM_LOG_EVENTS
#define NC_SCREEN
Definition WM_types.hh:374
#define NC_SCENE
Definition WM_types.hh:375
#define ND_WORKSPACE_DELETE
Definition WM_types.hh:426
#define NOTE_DATA
Definition WM_types.hh:406
ReportList * reports
Definition WM_types.hh:1025
#define ND_SPACE_INFO_REPORT
Definition WM_types.hh:517
#define ND_UNDO
Definition WM_types.hh:414
#define WM_EVENT_CURSOR_MOTION_THRESHOLD
Definition WM_types.hh:841
#define ND_FRAME
Definition WM_types.hh:431
#define NOTE_SUBTYPE
Definition WM_types.hh:554
wmOperatorCallContext
Definition WM_types.hh:236
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:239
@ WM_OP_EXEC_REGION_WIN
Definition WM_types.hh:246
@ WM_OP_INVOKE_SCREEN
Definition WM_types.hh:243
@ WM_OP_INVOKE_AREA
Definition WM_types.hh:242
@ WM_OP_EXEC_REGION_PREVIEW
Definition WM_types.hh:248
@ WM_OP_EXEC_SCREEN
Definition WM_types.hh:250
@ WM_OP_INVOKE_REGION_PREVIEW
Definition WM_types.hh:241
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
@ WM_OP_EXEC_REGION_CHANNELS
Definition WM_types.hh:247
@ WM_OP_INVOKE_REGION_CHANNELS
Definition WM_types.hh:240
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
@ WM_OP_EXEC_AREA
Definition WM_types.hh:249
#define ND_FILESAVE
Definition WM_types.hh:410
#define ND_WORKSPACE_SET
Definition WM_types.hh:425
#define NOTE_CATEGORY
Definition WM_types.hh:369
#define ND_LAYOUTBROWSE
Definition WM_types.hh:419
@ KM_TEXTINPUT
Definition WM_types.hh:301
#define NC_OBJECT
Definition WM_types.hh:376
#define ND_LAYOUTDELETE
Definition WM_types.hh:420
wmProgress
Definition WM_types.hh:847
#define NOTE_CATEGORY_TAG_CLEARED
Definition WM_types.hh:370
@ KM_NOTHING
Definition WM_types.hh:307
@ KM_ANY
Definition WM_types.hh:306
@ KM_PRESS
Definition WM_types.hh:308
@ KM_CLICK_DRAG
Definition WM_types.hh:316
@ KM_DBL_CLICK
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:309
@ KM_CLICK
Definition WM_types.hh:310
#define WM_UI_HANDLER_BREAK
Definition WM_types.hh:346
eWM_CursorWrapAxis
Definition WM_types.hh:225
@ WM_CURSOR_WRAP_X
Definition WM_types.hh:227
@ WM_CURSOR_WRAP_XY
Definition WM_types.hh:229
@ WM_CURSOR_WRAP_Y
Definition WM_types.hh:228
@ WM_CURSOR_WRAP_NONE
Definition WM_types.hh:226
#define NC_SPACE
Definition WM_types.hh:389
#define KM_MOD_HELD
Definition WM_types.hh:323
ListBase * ED_scene_markers_get(Scene *scene, ScrArea *area)
volatile int lock
#define U
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
BPy_StructRNA * depsgraph
@ NONE
unsigned long long int uint64_t
constexpr bool is_empty() const
void iter(FunctionRef< void(ID *old_id, ID *new_id)> func) const
@ FRONT
@ BACK
#define str(s)
#define UINT64_MAX
#define printf(...)
#define MEM_SAFE_FREE(v)
@ TOP
@ LEFT
@ RIGHT
format
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
#define SET(a, b, c, d, k, s, Ti)
#define G(x, y, z)
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRef prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
void storage_id_remap(ID *id_old, ID *id_new)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
float wrap(float value, float max, float min)
Definition node_math.h:71
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
std::string RNA_pointer_as_string_keywords(bContext *C, PointerRNA *ptr, const bool as_function, const bool all_args, const bool nested_args, const int max_prop_length)
int RNA_property_flag(PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
const char * RNA_property_identifier(const PropertyRNA *prop)
ARegionRuntimeHandle * runtime
CursorKeymapInfo_State state
char text[3][2][128]
const char * identifier
Definition RNA_types.hh:623
GHOST_TabletData tablet
GHOST_TabletData tablet
GHOST_TTrackpadEventSubTypes subtype
GHOST_TEventWheelAxis axis
GHOST_TTabletMode Active
Definition DNA_ID.h:404
void * last
void * first
ListBase wm
Definition BKE_main.hh:276
ListBase screens
Definition BKE_main.hh:261
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
struct wmTimer * reporttimer
struct CustomData_MeshMasks customdata_mask
struct ToolSettings * toolsettings
struct CustomData_MeshMasks customdata_mask_modal
ListBase areabase
struct bToolRef * tool
ListBase handlers
ListBase spacedata
short region_active_win
bScreen * full
ScrArea_Runtime runtime
ListBase regionbase
struct wmOperator * op
UndoStep * step_active
Wrapper for bScreen.
char do_draw_drag
ListBase regionbase
char skip_handling
struct wmTooltipState * tool_tip
ListBase areabase
struct ARegion * active_region
bToolRef_Runtime * runtime
int ymin
int ymax
int xmin
void label(blender::StringRef name, int icon)
uiLayout & row(bool align)
std::optional< bContextStore > context
wmOperatorCallParams optype_params
struct wmGizmoMap * gizmo_map
wmEventHandler_KeymapDynamicFn keymap_fn
void(* post_fn)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data)
wmEventHandler_KeymapDynamic dynamic
wmEventHandler_KeymapPost post
wmEventHandler head
struct wmEventHandler_Op::@201124376056252227312207212313325315250365150262 context
wmUIHandlerRemoveFunc remove_fn
struct wmEventHandler_UI::@362332347065273121377045151307270216115063355157 context
wmUIHandlerFunc handle_fn
wmEventHandler head
EventHandlerPoll poll
eWM_EventHandlerType type
eWM_EventHandlerFlag flag
wmEventType prev_type
Definition WM_types.hh:809
short customdata_free
Definition WM_types.hh:791
wmEventModifierFlag modifier
Definition WM_types.hh:771
wmEventType type
Definition WM_types.hh:754
short custom
Definition WM_types.hh:790
wmEventType prev_press_type
Definition WM_types.hh:822
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
wmEventType prev_press_keymodifier
Definition WM_types.hh:831
char utf8_buf[6]
Definition WM_types.hh:768
int prev_xy[2]
Definition WM_types.hh:817
wmEventModifierFlag prev_press_modifier
Definition WM_types.hh:829
wmTabletData tablet
Definition WM_types.hh:783
eWM_EventFlag flag
Definition WM_types.hh:785
short prev_val
Definition WM_types.hh:811
int prev_press_xy[2]
Definition WM_types.hh:827
wmEventType keymodifier
Definition WM_types.hh:780
int8_t direction
Definition WM_types.hh:774
void * customdata
Definition WM_types.hh:804
wmGizmoMapType_Params gzmap_params
eWM_GizmoFlagGroupTypeFlag flag
wmGizmoGroupType * type
wmGizmoGroup * parent_gzgroup
eWM_GizmoFlag flag
wmKeyMap * keymap
struct PointerRNA * ptr
bool(* poll_modal_item)(const struct wmOperator *op, int value)
const void * modal_items
unsigned int data
Definition WM_types.hh:355
unsigned int action
Definition WM_types.hh:355
const wmWindow * window
Definition WM_types.hh:353
unsigned int category
Definition WM_types.hh:355
unsigned int subtype
Definition WM_types.hh:355
void * reference
Definition WM_types.hh:357
wmOperatorType * optype
Definition WM_types.hh:1162
wmOperatorCallContext opcontext
Definition WM_types.hh:1164
PointerRNA * opptr
Definition WM_types.hh:1163
struct wmOperatorTypeMacro * next
struct IDProperty * properties
wmOperatorStatus(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1046
const char * idname
Definition WM_types.hh:1032
bool(* check)(bContext *C, wmOperator *op)
Definition WM_types.hh:1054
wmKeyMap * modalkeymap
Definition WM_types.hh:1142
wmOperatorStatus(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1062
ListBase macro
Definition WM_types.hh:1139
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1070
struct ReportList * reports
IDProperty * properties
struct wmOperator * next
struct wmOperator * prev
struct wmOperatorType * type
struct PointerRNA * ptr
struct wmOperator * opm
const wmNotifier * notifier
const wmNotifier * notifier
Definition BKE_screen.hh:77
char is_motion_absolute
Definition WM_types.hh:709
float pressure
Definition WM_types.hh:702
blender::float2 tilt
Definition WM_types.hh:707
void * customdata
Definition WM_types.hh:962
wmTimer * timer
Definition WM_types.hh:1424
struct wmMsgBus * message_bus
WindowManagerRuntimeHandle * runtime
struct UndoStack * undo_stack
struct wmWindow * winactive
struct wmKeyConfig * userconf
struct wmWindow * parent
WindowRuntimeHandle * runtime
uint64_t eventstate_prev_press_time_ms
struct wmEvent * eventstate
struct wmEvent * event_last_handled
struct Scene * scene
int event_queue_consecutive_gesture_xy[2]
char event_queue_check_drag_handled
short event_queue_consecutive_gesture_type
struct wmEvent_ConsecutiveData * event_queue_consecutive_gesture_data
ScrAreaMap global_areas
i
Definition text_draw.cc:230
void wm_operator_register(bContext *C, wmOperator *op)
Definition wm.cc:349
void WM_operator_free(wmOperator *op)
Definition wm.cc:275
void WM_cursor_modal_set(wmWindow *win, int val)
bool wm_cursor_arrow_move(wmWindow *win, const wmEvent *event)
void WM_cursor_modal_restore(wmWindow *win)
void WM_cursor_grab_enable(wmWindow *win, const eWM_CursorWrapAxis wrap, const rcti *wrap_region, const bool hide)
void WM_cursor_grab_disable(wmWindow *win, const int mouse_ungrab_xy[2])
void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop)
wmOperatorCallContext wm_drop_operator_context_get(const wmDropBox *)
void wm_drop_end(bContext *C, wmDrag *, wmDropBox *)
void wm_drags_check_ops(bContext *C, const wmEvent *event)
void wm_drags_exit(wmWindowManager *wm, wmWindow *win)
void WM_drag_free_list(ListBase *lb)
static ListBase dropboxes
int xy[2]
Definition wm_draw.cc:174
bool WM_region_use_viewport(ScrArea *area, ARegion *region)
Definition wm_draw.cc:564
void WM_paint_cursor_tag_redraw(wmWindow *win, ARegion *)
Definition wm_draw.cc:1573
float wm_pressure_curve(float raw_pressure)
bool WM_event_consecutive_gesture_test_break(const wmWindow *win, const wmEvent *event)
int WM_event_drag_direction(const wmEvent *event)
void WM_event_print(const wmEvent *event)
bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2])
int WM_userdef_event_map(int kmitype)
bool WM_event_consecutive_gesture_test(const wmEvent *event)
static eHandlerActionFlag wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHandler_Op *handler, int val)
wmOperatorStatus WM_operator_call_py(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, ReportList *reports, const bool is_undo)
bool WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
bool WM_window_modal_keymap_status_draw(bContext *C, wmWindow *win, uiLayout *layout)
constexpr wmTabletData wm_event_tablet_data_default()
wmEventHandler_Dropbox * WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
static void wm_event_custom_free(wmEvent *event)
wmOperatorStatus WM_operator_call_notest(bContext *C, wmOperator *op)
wmEventHandler_Keymap * WM_event_add_keymap_handler_poll(ListBase *handlers, wmKeyMap *keymap, EventHandlerPoll poll)
static void wm_handler_op_context_get_if_valid(bContext *C, wmEventHandler_Op *handler, const wmEvent *event, ScrArea **r_area, ARegion **r_region)
wmEventHandler_Keymap * WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
void WM_menu_name_call(bContext *C, const char *menu_name, short context)
bool WM_operator_ui_poll(wmOperatorType *ot, PointerRNA *ptr)
static void wm_eventemulation(wmEvent *event, bool test_only)
static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
static void wm_operator_reports(bContext *C, wmOperator *op, const wmOperatorStatus retval, const bool caller_owns_reports)
static wmEvent * wm_event_add_mousemove_to_head(wmWindow *win)
static wmEvent * wm_event_add_intern(wmWindow *win, const wmEvent *event_to_add)
void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
static void wm_handler_operator_insert(wmWindow *win, wmEventHandler_Op *handler)
void wm_event_do_handlers(bContext *C)
bool WM_operator_poll(bContext *C, wmOperatorType *ot)
bool WM_operator_repeat_check(const bContext *, wmOperator *op)
static eHandlerActionFlag wm_event_do_handlers_area_regions(bContext *C, wmEvent *event, ScrArea *area)
static intptr_t wm_operator_undo_active_id(const wmWindowManager *wm)
static ScrArea * area_event_inside(bContext *C, const int xy[2])
void WM_window_status_area_tag_redraw(wmWindow *win)
wmEvent * WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add)
BLI_INLINE bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void * WM_event_consecutive_data_get(wmWindow *win, const char *id)
wmEventHandler_UI * WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const eWM_EventHandlerFlag flag)
static void wm_event_modalkeymap_end(wmEvent *event, const wmEvent_ModalMapStore *event_backup)
static std::string keymap_handler_log_kmi_op_str(bContext *C, const wmKeyMapItem *kmi)
void WM_main_remap_editor_id_reference(const blender::bke::id::IDRemapper &mappings)
static eHandlerActionFlag wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler_Op *handler, const wmEvent *event)
static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent &event)
void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
void WM_report_banners_cancel(Main *bmain)
#define CASE_BUTTON(ghost_button, type)
bool WM_operator_name_poll(bContext *C, const char *opstring)
static void ui_handler_wait_for_input_remove(bContext *C, void *userdata)
void WM_event_remove_area_handler(ListBase *handlers, void *area)
wmOperatorStatus WM_operator_repeat_last(bContext *C, wmOperator *op)
static eHandlerActionFlag wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, ListBase *handlers)
static void wm_event_add_notifier_intern(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
wmOperator * WM_operator_find_modal_by_type(wmWindow *win, const wmOperatorType *ot)
static void wm_region_tag_draw_on_gizmo_delay_refresh_for_tweak(wmWindow *win)
static eHandlerActionFlag wm_handlers_do_gizmo_handler(bContext *C, wmWindowManager *wm, wmEventHandler_Gizmo *handler, wmEvent *event, const bool always_pass, ListBase *handlers, const bool do_debug_handler)
void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
static void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result, const bool with_gizmos)
bool WM_event_handler_region_marker_poll(const wmWindow *win, const ScrArea *area, const ARegion *region, const wmEvent *event)
static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store, const bool has_undo_step, const bool has_register)
static wmOperatorStatus wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, const wmOperatorCallContext context, const bool poll_only, const wmEvent *event)
static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state)
void WM_global_report(eReportType type, const char *message)
void wm_tablet_data_from_ghost(const GHOST_TabletData *tablet_data, wmTabletData *wmtab)
static eHandlerActionFlag 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)
void WM_event_consecutive_data_free(wmWindow *win)
static eHandlerActionFlag wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
static bool wm_action_not_handled(const eHandlerActionFlag action)
static int ui_handler_wait_for_input(bContext *C, const wmEvent *event, void *userdata)
void WM_event_get_keymaps_from_handler(wmWindowManager *wm, wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result)
wmOperatorStatus WM_operator_call(bContext *C, wmOperator *op)
void WM_event_free_ui_handler_all(bContext *C, ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn)
void WM_reports_from_reports_move(wmWindowManager *wm, ReportList *reports)
static wmWindow * wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
static ARegion * region_event_inside(bContext *C, const int xy[2])
void wm_event_free_all(wmWindow *win)
void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result)
void WM_event_tablet_data_default_set(wmTabletData *tablet_data)
static void wm_event_cursor_store(CursorKeymapInfo_State *state, const wmEvent *event, short space_type, short region_type, const bToolRef *tref)
static void wm_region_mouse_co(bContext *C, wmEvent *event)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const wmEvent *event)
void WM_event_modal_handler_region_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region)
static uint note_hash_for_queue_fn(const void *ptr)
wmOperatorStatus WM_operator_call_ex(bContext *C, wmOperator *op, const bool store)
static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
wmEventHandler_Keymap * WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int)
void WM_set_locked_interface_with_flags(wmWindowManager *wm, short lock_flags)
void WM_event_remove_modal_handler_all(const wmOperator *op, const bool postpone)
static eHandlerActionFlag wm_handler_ui_call(bContext *C, wmEventHandler_UI *handler, const wmEvent *event, const bool always_pass)
wmEventHandler_Op * WM_event_add_modal_handler_ex(wmWindow *win, ScrArea *area, ARegion *region, wmOperator *op)
const char * WM_window_cursor_keymap_status_get(const wmWindow *win, int button_index, int type_index)
void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, const int eventval)
static void wm_event_free_last(wmWindow *win)
static eHandlerActionFlag wm_handlers_do_keymap_with_keymap_handler(bContext *C, wmEvent *event, ListBase *handlers, wmEventHandler_Keymap *handler, wmKeyMap *keymap, const bool do_debug_handler)
static void wm_event_state_update_and_click_set(wmEvent *event, uint64_t event_time_ms, wmEvent *event_state, uint64_t *event_state_prev_press_time_ms_p, const GHOST_TEventType type)
static void wm_paintcursor_tag(bContext *C, wmWindowManager *wm, ARegion *region)
static eHandlerActionFlag wm_event_do_region_handlers(bContext *C, wmEvent *event, ARegion *region)
static bool wm_event_is_double_click(const wmEvent *event, const uint64_t event_time_ms, const uint64_t event_prev_press_time_ms)
wmOperatorStatus WM_operator_repeat(bContext *C, wmOperator *op)
#define PRINT
void WM_operator_region_active_win_set(bContext *C)
static wmKeyMapItem * wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, wmOperator *op, const wmEvent *event)
wmKeyMapItem * WM_event_match_keymap_item_from_handlers(bContext *C, wmWindowManager *wm, wmWindow *win, ListBase *handlers, const wmEvent *event)
wmEvent * WM_event_add(wmWindow *win, const wmEvent *event_to_add)
static wmEventType wm_event_type_from_ghost_key(GHOST_TKey key)
static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot)
static void wm_event_prev_click_set(uint64_t event_time_ms, wmEvent *event_state, uint64_t *r_event_state_prev_press_time_ms)
static std::optional< std::string > keymap_handler_log_kmi_event_str(const wmKeyMapItem *kmi)
static bool wm_event_is_same_key_press(const wmEvent &event_a, const wmEvent &event_b)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
static wmOperatorStatus wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, const bool store)
static void wm_event_timers_execute(bContext *C)
static void wm_event_handler_ui_cancel(bContext *C)
wmEventHandler_Keymap * WM_event_add_keymap_handler_v2d_mask(ListBase *handlers, wmKeyMap *keymap)
static bool note_cmp_for_queue_fn(const void *a, const void *b)
void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc handle_fn, wmUIHandlerRemoveFunc remove_fn, void *user_data, const bool postpone)
static void wm_notifier_clear(wmNotifier *note)
eHandlerActionFlag
@ WM_HANDLER_HANDLED
@ WM_HANDLER_BREAK
@ WM_HANDLER_MODAL
void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area)
static eHandlerActionFlag wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler_base, wmEvent *event, PointerRNA *properties, const char *kmi_idname)
bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
static void wm_event_free_last_handled(wmWindow *win, wmEvent *event)
static eHandlerActionFlag wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
void WM_global_reportf(eReportType type, const char *format,...)
void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, const bool postpone)
void WM_main_remove_notifier_reference(const void *reference)
static void wm_event_state_update_and_click_set_ex(wmEvent *event, uint64_t event_time_ms, wmEvent *event_state, uint64_t *event_state_prev_press_time_ms_p, const bool is_keyboard, const bool check_double_click)
void WM_report_banner_show(wmWindowManager *wm, wmWindow *win)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void wm_event_handler_ui_cancel_ex(bContext *C, wmWindow *win, ARegion *region, bool reactivate_button)
bool WM_event_handler_region_v2d_mask_poll(const wmWindow *, const ScrArea *, const ARegion *region, const wmEvent *event)
wmEventHandler_Keymap * WM_event_add_keymap_handler_dynamic(ListBase *handlers, wmEventHandler_KeymapDynamicFn keymap_fn, void *user_data)
static void wm_paintcursor_test(bContext *C, const wmEvent *event)
void wm_event_free(wmEvent *event)
static wmWindow * wm_event_find_fileselect_root_window_from_context(const bContext *C)
void WM_operator_name_call_ptr_with_depends_on_cursor(bContext *C, wmOperatorType *ot, wmOperatorCallContext opcontext, PointerRNA *properties, const wmEvent *event, const StringRef drawstr)
static wmEvent * wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int deltax, int deltay)
static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect)
ScrArea * WM_window_status_area_find(wmWindow *win, bScreen *screen)
static bool wm_notifier_is_clear(const wmNotifier *note)
static wmEventType wm_event_type_from_ghost_button(const GHOST_TButton button, const wmEventType fallback)
void WM_event_set_keymap_handler_post_callback(wmEventHandler_Keymap *handler, void(keymap_tag)(wmKeyMap *keymap, wmKeyMapItem *kmi, void *user_data), void *user_data)
static void wm_operator_free_for_fileselect(wmOperator *file_operator)
BLI_INLINE void wm_event_handler_return_value_check(const bContext *C, const wmEvent *event, const eHandlerActionFlag action)
#define WM_HANDLER_CONTINUE
static intptr_t wm_operator_register_active_id(const wmWindowManager *wm)
static bool screen_temp_region_exists(const ARegion *region)
static wmOperator * wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
wmKeyMapItem * WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wmEvent *event)
void wm_event_free_handler(wmEventHandler *handler)
static void wm_event_modalkeymap_begin(const bContext *C, wmOperator *op, wmEvent *event, wmEvent_ModalMapStore *event_backup)
void wm_event_do_notifiers(bContext *C)
void WM_event_consecutive_data_set(wmWindow *win, const char *id, void *custom_data)
static const char * keymap_handler_log_action_str(const eHandlerActionFlag action)
static wmOperatorStatus wm_operator_invoke(bContext *C, wmOperatorType *ot, const wmEvent *event, PointerRNA *properties, ReportList *reports, const bool poll_only, bool use_last_properties)
static wmOperatorStatus wm_operator_exec_notest(bContext *C, wmOperator *op)
wmOperatorStatus WM_operator_name_call_with_properties(bContext *C, const char *opstring, wmOperatorCallContext context, IDProperty *properties, const wmEvent *event)
static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event)
void WM_event_ui_handler_region_popup_replace(wmWindow *win, const ARegion *old_region, ARegion *new_region)
static void wm_event_custom_clear(wmEvent *event)
void WM_event_get_keymap_from_toolsystem_with_gizmos(wmWindowManager *wm, wmWindow *win, wmEventHandler_Keymap *handler, wmEventHandler_KeymapResult *km_result)
bool WM_event_match(const wmEvent *winevent, const wmKeyMapItem *kmi)
void WM_set_locked_interface(wmWindowManager *wm, bool lock)
bool WM_event_handler_region_v2d_mask_no_marker_poll(const wmWindow *win, const ScrArea *area, const ARegion *region, const wmEvent *event)
static wmEvent * wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, const int type, const void *customdata, const uint64_t event_time_ms)
static bool wm_event_always_pass(const wmEvent *event)
void wm_event_init_from_window(wmWindow *win, wmEvent *event)
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
void WM_event_add_mousemove(wmWindow *win)
static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect)
@ WM_HANDLER_TYPE_UI
@ WM_HANDLER_TYPE_OP
@ WM_HANDLER_TYPE_KEYMAP
@ WM_HANDLER_TYPE_GIZMO
@ WM_HANDLER_TYPE_DROPBOX
#define ISMOUSE_BUTTON(event_type)
@ EVT_FILESELECT_FULL_OPEN
@ EVT_FILESELECT_CANCEL
@ EVT_FILESELECT_EXTERNAL_CANCEL
@ EVT_FILESELECT_EXEC
#define ISKEYBOARD_OR_BUTTON(event_type)
#define ISMOUSE_MOTION(event_type)
#define ISMOUSE_WHEEL(event_type)
@ EVT_TABLET_NONE
@ EVT_TABLET_STYLUS
@ EVT_TABLET_ERASER
@ EVT_DATA_NDOF_MOTION
@ EVT_DATA_DRAGDROP
@ EVT_DATA_XR
#define ISKEYMODIFIER(event_type)
wmEventType
@ EVT_PAD8
@ EVT_PAD2
@ MOUSEPAN
@ BUTTON7MOUSE
@ RIGHTMOUSE
@ EVT_SIXKEY
@ EVT_BUT_CANCEL
@ EVT_QUOTEKEY
@ EVT_PADPERIOD
@ TIMER
@ EVT_PAD4
@ EVT_PADASTERKEY
@ BUTTON6MOUSE
@ EVT_PLUSKEY
@ EVT_PAD0
@ EVT_FOURKEY
@ EVT_MODAL_MAP
@ EVT_RIGHTCTRLKEY
@ WM_IME_COMPOSITE_EVENT
@ WHEELLEFTMOUSE
@ MOUSEZOOM
@ EVT_F1KEY
@ EVT_PERIODKEY
@ EVT_PADSLASHKEY
@ WHEELRIGHTMOUSE
@ EVT_COMMAKEY
@ EVT_MEDIAPLAY
@ EVT_LEFTBRACKETKEY
@ EVT_MEDIAFIRST
@ EVT_ZEROKEY
@ EVT_PAD9
@ EVT_DELKEY
@ EVT_SEVENKEY
@ EVT_TABKEY
@ EVT_DOWNARROWKEY
@ EVT_MEDIASTOP
@ EVT_AKEY
@ EVT_PAD3
@ EVT_MINUSKEY
@ MOUSEROTATE
@ WHEELUPMOUSE
@ EVT_PAGEUPKEY
@ EVT_OSKEY
@ EVT_PAGEDOWNKEY
@ EVT_LEFTCTRLKEY
@ TABLET_ERASER
@ EVT_EQUALKEY
@ EVT_RIGHTARROWKEY
@ EVT_PADENTER
@ EVT_NINEKEY
@ EVT_SPACEKEY
@ EVT_CAPSLOCKKEY
@ BUTTON4MOUSE
@ WHEELDOWNMOUSE
@ EVT_PAD6
@ EVT_PAD5
@ EVT_HOMEKEY
@ TABLET_STYLUS
@ EVENT_NONE
@ MOUSEMOVE
@ EVT_FILESELECT
@ EVT_RIGHTBRACKETKEY
@ EVT_UNKNOWNKEY
@ WM_IME_COMPOSITE_END
@ EVT_ENDKEY
@ EVT_RIGHTALTKEY
@ EVT_MEDIALAST
@ WM_IME_COMPOSITE_START
@ EVT_LINEFEEDKEY
@ MOUSESMARTZOOM
@ EVT_BACKSLASHKEY
@ EVT_FIVEKEY
@ EVT_APPKEY
@ EVT_ACCENTGRAVEKEY
@ EVT_PADMINUS
@ EVT_ONEKEY
@ EVT_UPARROWKEY
@ LEFTMOUSE
@ EVT_SLASHKEY
@ EVT_EIGHTKEY
@ EVT_LEFTARROWKEY
@ NDOF_MOTION
@ EVT_INSERTKEY
@ MIDDLEMOUSE
@ TIMERREPORT
@ EVT_LEFTALTKEY
@ EVT_ESCKEY
@ EVT_THREEKEY
@ EVT_BACKSPACEKEY
@ INBETWEEN_MOUSEMOVE
@ EVT_GIZMO_UPDATE
@ EVT_DROP
@ EVT_PAD1
@ EVT_TWOKEY
@ EVT_GRLESSKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_PAUSEKEY
@ EVT_PAD7
@ EVT_LEFTSHIFTKEY
@ EVT_PADPLUSKEY
@ WINDEACTIVATE
@ EVT_SEMICOLONKEY
@ EVT_XR_ACTION
@ EVT_HYPER
@ EVT_RETKEY
@ BUTTON5MOUSE
#define ISTIMER(event_type)
#define ISMOUSE_GESTURE(event_type)
#define ISKEYBOARD(event_type)
#define ISMOUSE(event_type)
PointerRNA * ptr
Definition wm_files.cc:4226
void wm_test_autorun_warning(bContext *C)
Definition wm_files.cc:4262
wmOperatorType * ot
Definition wm_files.cc:4225
void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup)
bool WM_gizmo_group_type_ensure_ptr_ex(wmGizmoGroupType *gzgt, wmGizmoMapType *gzmap_type)
wmGizmoGroup * WM_gizmomaptype_group_init_runtime_with_region(wmGizmoMapType *gzmap_type, wmGizmoGroupType *gzgt, ARegion *region)
bool wm_gizmogroup_is_any_selected(const wmGizmoGroup *gzgroup)
wmGizmoGroupType * WM_gizmogrouptype_find(const StringRef idname, bool quiet)
const ListBase * WM_gizmomap_group_list(wmGizmoMap *gzmap)
bool WM_gizmomap_is_any_selected(const wmGizmoMap *gzmap)
bool wm_gizmomap_highlight_handled(wmGizmoMap *gzmap)
void wm_gizmomaps_handled_modal_update(bContext *C, wmEvent *event, wmEventHandler_Op *handler)
bool wm_gizmomap_highlight_set(wmGizmoMap *gzmap, const bContext *C, wmGizmo *gz, int part)
wmGizmoGroup * WM_gizmomap_group_find(wmGizmoMap *gzmap, const char *idname)
bool WM_gizmomap_tag_delay_refresh_for_tweak_check(wmGizmoMap *gzmap)
wmGizmo * wm_gizmomap_highlight_find(wmGizmoMap *gzmap, bContext *C, const wmEvent *event, int *r_part)
void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
wmGizmo * wm_gizmomap_highlight_get(wmGizmoMap *gzmap)
void WM_gizmoconfig_update(Main *bmain)
ARegion * WM_gizmomap_tooltip_init(bContext *C, ARegion *region, int *, double *, bool *r_exit_on_event)
bool wm_gizmomap_highlight_pending(const wmGizmoMap *gzmap)
wmGizmoMapType * WM_gizmomaptype_ensure(const wmGizmoMapType_Params *gzmap_params)
void wm_gizmomap_handler_context_gizmo(bContext *, wmEventHandler_Gizmo *)
wmGizmo * wm_gizmomap_modal_get(wmGizmoMap *gzmap)
std::optional< std::string > WM_modalkeymap_operator_items_to_string(wmOperatorType *ot, const int propvalue, const bool compact)
wmKeyMap * WM_keymap_list_find_spaceid_or_empty(ListBase *lb, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:877
void WM_keyconfig_update(wmWindowManager *wm)
bool WM_keymap_poll(bContext *C, wmKeyMap *keymap)
Definition wm_keymap.cc:470
wmKeyMap * WM_keymap_active(const wmWindowManager *wm, wmKeyMap *keymap)
std::optional< std::string > WM_keymap_item_to_string(const wmKeyMapItem *kmi, const bool compact)
void WM_msg_id_update(wmMsgBus *mbus, ID *id_src, ID *id_dst)
void WM_msgbus_handle(wmMsgBus *mbus, bContext *C)
void WM_msg_id_remove(wmMsgBus *mbus, const ID *id)
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
bool WM_operator_depends_on_cursor(bContext &C, wmOperatorType &ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
bool WM_operator_last_properties_store(wmOperator *op)
wmOperator * WM_operator_last_redo(const bContext *C)
bool WM_operator_last_properties_init(wmOperator *op)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
std::string WM_operator_pystring(bContext *C, wmOperator *op, const bool all_args, const bool macro_args)
void WM_operator_properties_free(PointerRNA *ptr)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2])
Definition wm_stereo.cc:164
void wm_surfaces_do_depsgraph(bContext *C)
Definition wm_surface.cc:42
bToolRef * WM_toolsystem_ref_find(WorkSpace *workspace, const bToolKey *tkey)
int WM_toolsystem_mode_from_spacetype(const Scene *scene, ViewLayer *view_layer, ScrArea *area, int space_type)
void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref, PointerRNA *dst_ptr, PointerRNA *src_ptr, wmOperatorType *ot)
bToolRef * WM_toolsystem_ref_from_context(const bContext *C)
void WM_tooltip_clear(bContext *C, wmWindow *win)
Definition wm_tooltip.cc:82
void WM_tooltip_init(bContext *C, wmWindow *win)
Definition wm_tooltip.cc:97
void WM_tooltip_timer_init(bContext *C, wmWindow *win, ScrArea *area, ARegion *region, wmTooltipInitFn init)
Definition wm_tooltip.cc:64
blender::int2 WM_window_native_pixel_size(const wmWindow *win)
void wm_cursor_position_from_ghost_screen_coords(wmWindow *win, int *x, int *y)
void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
void wm_test_gpu_backend_fallback(bContext *C)
WorkSpaceLayout * WM_window_get_active_layout(const wmWindow *win)
void wm_test_opengl_deprecation_warning(bContext *C)
bool wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:433
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const wmEventType event_type, const double time_step)
void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
bool WM_window_is_temp_screen(const wmWindow *win)
wmWindow * WM_window_find_under_cursor(wmWindow *win, const int event_xy[2], int r_event_xy_other[2])
eWM_CapabilitiesFlag WM_capabilities_flag()
ViewLayer * WM_window_get_active_view_layer(const wmWindow *win)
Scene * WM_window_get_active_scene(const wmWindow *win)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
void WM_window_title(wmWindowManager *wm, wmWindow *win, const char *title)
Definition wm_window.cc:489
WorkSpace * WM_window_get_active_workspace(const wmWindow *win)
bScreen * WM_window_get_active_screen(const wmWindow *win)
uint8_t flag
Definition wm_window.cc:139
ScrArea * WM_xr_session_area_get(const wmXrData *xr)