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