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