2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2007 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/windowmanager/intern/wm_event_system.c
30 * Handle events and notifiers from GHOST input (mouse, keyboard, tablet, ndof).
32 * Also some operator reports utility functions.
38 #include "DNA_listBase.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_windowmanager_types.h"
42 #include "DNA_userdef_types.h"
44 #include "MEM_guardedalloc.h"
48 #include "GHOST_C-api.h"
50 #include "BLI_blenlib.h"
51 #include "BLI_dynstr.h"
52 #include "BLI_utildefines.h"
55 #include "BKE_context.h"
56 #include "BKE_idprop.h"
57 #include "BKE_global.h"
58 #include "BKE_layer.h"
60 #include "BKE_report.h"
61 #include "BKE_scene.h"
62 #include "BKE_screen.h"
63 #include "BKE_workspace.h"
65 #include "BKE_sound.h"
67 #include "ED_fileselect.h"
69 #include "ED_screen.h"
70 #include "ED_view3d.h"
74 #include "RNA_access.h"
76 #include "UI_interface.h"
82 #include "WM_message.h"
84 #include "wm_window.h"
85 #include "wm_event_system.h"
86 #include "wm_event_types.h"
88 #include "RNA_enum_types.h"
90 #include "DEG_depsgraph.h"
92 /* Motion in pixels allowed before we don't consider single/double click. */
93 #define WM_EVENT_CLICK_WIGGLE_ROOM 2
95 static void wm_notifier_clear(wmNotifier *note);
96 static void update_tablet_data(wmWindow *win, wmEvent *event);
98 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports,
99 const short context, const bool poll_only);
101 /* ************ event management ************** */
103 wmEvent *wm_event_add_ex(wmWindow *win, const wmEvent *event_to_add, const wmEvent *event_to_add_after)
105 wmEvent *event = MEM_mallocN(sizeof(wmEvent), "wmEvent");
107 *event = *event_to_add;
109 update_tablet_data(win, event);
111 if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
112 /* We could have a preference to support relative tablet motion (we can't detect that). */
113 event->is_motion_absolute = (
114 (event->tablet_data != NULL) &&
115 (event->tablet_data->Active != GHOST_kTabletModeNone));
118 if (event_to_add_after == NULL) {
119 BLI_addtail(&win->queue, event);
122 /* note, strictly speaking this breaks const-correctness, however we're only changing 'next' member */
123 BLI_insertlinkafter(&win->queue, (void *)event_to_add_after, event);
128 wmEvent *wm_event_add(wmWindow *win, const wmEvent *event_to_add)
130 return wm_event_add_ex(win, event_to_add, NULL);
133 void wm_event_free(wmEvent *event)
135 if (event->customdata) {
136 if (event->customdatafree) {
137 /* note: pointer to listbase struct elsewhere */
138 if (event->custom == EVT_DATA_DRAGDROP) {
139 ListBase *lb = event->customdata;
140 WM_drag_free_list(lb);
143 MEM_freeN(event->customdata);
148 if (event->tablet_data) {
149 MEM_freeN((void *)event->tablet_data);
155 void wm_event_free_all(wmWindow *win)
159 while ((event = BLI_pophead(&win->queue))) {
160 wm_event_free(event);
164 void wm_event_init_from_window(wmWindow *win, wmEvent *event)
166 /* make sure we don't copy any owned pointers */
167 BLI_assert(win->eventstate->tablet_data == NULL);
169 *event = *(win->eventstate);
172 /* ********************* notifiers, listeners *************** */
174 static bool wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
178 for (note = wm->queue.first; note; note = note->next)
179 if ((note->category | note->data | note->subtype | note->action) == type && note->reference == reference)
185 /* XXX: in future, which notifiers to send to other windows? */
186 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
188 wmWindowManager *wm = CTX_wm_manager(C);
191 if (wm_test_duplicate_notifier(wm, type, reference))
194 note = MEM_callocN(sizeof(wmNotifier), "notifier");
197 BLI_addtail(¬e->wm->queue, note);
199 note->window = CTX_wm_window(C);
201 note->category = type & NOTE_CATEGORY;
202 note->data = type & NOTE_DATA;
203 note->subtype = type & NOTE_SUBTYPE;
204 note->action = type & NOTE_ACTION;
206 note->reference = reference;
209 void WM_main_add_notifier(unsigned int type, void *reference)
211 Main *bmain = G.main;
212 wmWindowManager *wm = bmain->wm.first;
215 if (!wm || wm_test_duplicate_notifier(wm, type, reference))
218 note = MEM_callocN(sizeof(wmNotifier), "notifier");
221 BLI_addtail(¬e->wm->queue, note);
223 note->category = type & NOTE_CATEGORY;
224 note->data = type & NOTE_DATA;
225 note->subtype = type & NOTE_SUBTYPE;
226 note->action = type & NOTE_ACTION;
228 note->reference = reference;
232 * Clear notifiers by reference, Used so listeners don't act on freed data.
234 void WM_main_remove_notifier_reference(const void *reference)
236 Main *bmain = G.main;
237 wmWindowManager *wm = bmain->wm.first;
240 wmNotifier *note, *note_next;
242 for (note = wm->queue.first; note; note = note_next) {
243 note_next = note->next;
245 if (note->reference == reference) {
246 /* don't remove because this causes problems for #wm_event_do_notifiers
247 * which may be looping on the data (deleting screens) */
248 wm_notifier_clear(note);
254 if (wm->message_bus) {
255 WM_msg_id_remove(wm->message_bus, reference);
262 void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id)
264 Main *bmain = G.main;
267 for (sc = bmain->screen.first; sc; sc = sc->id.next) {
270 for (sa = sc->areabase.first; sa; sa = sa->next) {
273 for (sl = sa->spacedata.first; sl; sl = sl->next) {
274 ED_spacedata_id_remap(sa, sl, old_id, new_id);
279 wmWindowManager *wm = bmain->wm.first;
280 if (wm && wm->message_bus) {
281 struct wmMsgBus *mbus = wm->message_bus;
282 if (new_id != NULL) {
283 WM_msg_id_update(mbus, old_id, new_id);
286 WM_msg_id_remove(mbus, old_id);
291 static void wm_notifier_clear(wmNotifier *note)
293 /* NULL the entire notifier, only leaving (next, prev) members intact */
294 memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
298 * Was part of #wm_event_do_notifiers, split out so it can be called once before entering the #WM_main loop.
299 * This ensures operators don't run before the UI and depsgraph are initialized.
301 void wm_event_do_refresh_wm_and_depsgraph(bContext *C)
303 wmWindowManager *wm = CTX_wm_manager(C);
304 uint64_t win_combine_v3d_datamask = 0;
306 /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
307 for (wmWindow *win = wm->windows.first; win; win = win->next) {
308 const Scene *scene = WM_window_get_active_scene(win);
309 const bScreen *screen = WM_window_get_active_screen(win);
311 win_combine_v3d_datamask |= ED_view3d_screen_datamask(scene, screen);
314 /* cached: editor refresh callbacks now, they get context */
315 for (wmWindow *win = wm->windows.first; win; win = win->next) {
316 const bScreen *screen = WM_window_get_active_screen(win);
317 Scene *scene = WM_window_get_active_scene(win);
320 CTX_wm_window_set(C, win);
321 for (sa = screen->areabase.first; sa; sa = sa->next) {
322 if (sa->do_refresh) {
323 CTX_wm_area_set(C, sa);
324 ED_area_do_refresh(C, sa);
328 /* XXX make lock in future, or separated derivedmesh users in scene */
329 if (G.is_rendering == false) {
330 /* depsgraph & animation: update tagged datablocks */
331 Main *bmain = CTX_data_main(C);
333 /* copied to set's in scene_update_tagged_recursive() */
334 scene->customdata_mask = win_combine_v3d_datamask;
336 /* XXX, hack so operators can enforce datamasks [#26482], gl render */
337 scene->customdata_mask |= scene->customdata_mask_modal;
339 WorkSpace *workspace = WM_window_get_active_workspace(win);
341 BKE_workspace_update_tagged(bmain, workspace, scene);
345 CTX_wm_window_set(C, NULL);
348 /* called in mainloop */
349 void wm_event_do_notifiers(bContext *C)
351 wmWindowManager *wm = CTX_wm_manager(C);
352 wmNotifier *note, *next;
358 /* disable? - keep for now since its used for window level notifiers. */
360 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
361 for (win = wm->windows.first; win; win = win->next) {
362 Scene *scene = WM_window_get_active_scene(win);
363 bool do_anim = false;
365 CTX_wm_window_set(C, win);
367 for (note = wm->queue.first; note; note = next) {
370 if (note->category == NC_WM) {
371 if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
373 wm_window_title(wm, win);
375 else if (note->data == ND_DATACHANGED)
376 wm_window_title(wm, win);
378 if (note->window == win) {
379 if (note->category == NC_SCREEN) {
380 if (note->data == ND_WORKSPACE_SET) {
381 WorkSpace *ref_ws = note->reference;
383 UI_popup_handlers_remove_all(C, &win->modalhandlers);
385 ED_workspace_change(ref_ws, C, wm, win);
386 if (G.debug & G_DEBUG_EVENTS)
387 printf("%s: Workspace set %p\n", __func__, note->reference);
389 else if (note->data == ND_WORKSPACE_DELETE) {
390 WorkSpace *workspace = note->reference;
392 ED_workspace_delete(workspace, CTX_data_main(C), C, wm); // XXX hrms, think this over!
393 if (G.debug & G_DEBUG_EVENTS)
394 printf("%s: Workspace delete %p\n", __func__, workspace);
396 else if (note->data == ND_LAYOUTBROWSE) {
397 bScreen *ref_screen = BKE_workspace_layout_screen_get(note->reference);
399 /* free popup handlers only [#35434] */
400 UI_popup_handlers_remove_all(C, &win->modalhandlers);
403 ED_screen_change(C, ref_screen); /* XXX hrms, think this over! */
404 if (G.debug & G_DEBUG_EVENTS)
405 printf("%s: screen set %p\n", __func__, note->reference);
407 else if (note->data == ND_LAYOUTDELETE) {
408 WorkSpace *workspace = WM_window_get_active_workspace(win);
409 WorkSpaceLayout *layout = note->reference;
411 ED_workspace_layout_delete(workspace, layout, C); // XXX hrms, think this over!
412 if (G.debug & G_DEBUG_EVENTS)
413 printf("%s: screen delete %p\n", __func__, note->reference);
418 if (note->window == win ||
419 (note->window == NULL && (note->reference == NULL || note->reference == scene)))
421 if (note->category == NC_SCENE) {
422 if (note->data == ND_FRAME)
426 if (ELEM(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_WM)) {
427 ViewLayer *view_layer = CTX_data_view_layer(C);
428 ED_info_stats_clear(view_layer);
429 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL);
434 /* XXX, quick frame changes can cause a crash if framechange and rendering
435 * collide (happens on slow scenes), BKE_scene_graph_update_for_newframe can be called
436 * twice which can depgraph update the same object at once */
437 if (G.is_rendering == false) {
438 /* depsgraph gets called, might send more notifiers */
439 Depsgraph *depsgraph = CTX_data_depsgraph(C);
440 ED_update_for_newframe(CTX_data_main(C), depsgraph);
445 /* the notifiers are sent without context, to keep it clean */
446 while ((note = BLI_pophead(&wm->queue))) {
447 for (win = wm->windows.first; win; win = win->next) {
448 Scene *scene = WM_window_get_active_scene(win);
449 bScreen *screen = WM_window_get_active_screen(win);
450 WorkSpace *workspace = WM_window_get_active_workspace(win);
452 /* filter out notifiers */
453 if (note->category == NC_SCREEN &&
455 note->reference != screen &&
456 note->reference != workspace &&
457 note->reference != WM_window_get_active_layout(win))
461 else if (note->category == NC_SCENE && note->reference && note->reference != scene) {
467 /* XXX context in notifiers? */
468 CTX_wm_window_set(C, win);
470 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name + 2, note->category); */
471 ED_screen_do_listen(C, note);
473 for (ar = screen->regionbase.first; ar; ar = ar->next) {
474 ED_region_do_listen(screen, NULL, ar, note, scene);
477 ED_screen_areas_iter(win, screen, sa) {
478 ED_area_do_listen(screen, sa, note, scene, workspace);
479 for (ar = sa->regionbase.first; ar; ar = ar->next) {
480 ED_region_do_listen(screen, sa, ar, note, scene);
488 #endif /* if 1 (postpone disabling for in favor of message-bus), eventually. */
490 /* Handle message bus. */
492 for (win = wm->windows.first; win; win = win->next) {
493 CTX_wm_window_set(C, win);
494 WM_msgbus_handle(wm->message_bus, C);
496 CTX_wm_window_set(C, NULL);
499 wm_event_do_refresh_wm_and_depsgraph(C);
502 static int wm_event_always_pass(const wmEvent *event)
504 /* some events we always pass on, to ensure proper communication */
505 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
508 /* ********************* ui handler ******************* */
510 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, const wmEvent *event, int always_pass)
512 ScrArea *area = CTX_wm_area(C);
513 ARegion *region = CTX_wm_region(C);
514 ARegion *menu = CTX_wm_menu(C);
515 static bool do_wheel_ui = true;
516 const bool is_wheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN);
519 /* UI code doesn't handle return values - it just always returns break.
520 * to make the DBL_CLICK conversion work, we just don't send this to UI, except mouse clicks */
521 if (((handler->flag & WM_HANDLER_ACCEPT_DBL_CLICK) == 0) &&
522 (event->type != LEFTMOUSE) &&
523 (event->val == KM_DBL_CLICK))
525 return WM_HANDLER_CONTINUE;
528 /* UI is quite aggressive with swallowing events, like scrollwheel */
529 /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
530 if (do_wheel_ui == false) {
532 return WM_HANDLER_CONTINUE;
533 else if (wm_event_always_pass(event) == 0)
537 /* we set context to where ui handler came from */
538 if (handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
539 if (handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
540 if (handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
542 retval = handler->ui_handle(C, event, handler->ui_userdata);
544 /* putting back screen context */
545 if ((retval != WM_UI_HANDLER_BREAK) || always_pass) {
546 CTX_wm_area_set(C, area);
547 CTX_wm_region_set(C, region);
548 CTX_wm_menu_set(C, menu);
551 /* this special cases is for areas and regions that get removed */
552 CTX_wm_area_set(C, NULL);
553 CTX_wm_region_set(C, NULL);
554 CTX_wm_menu_set(C, NULL);
557 if (retval == WM_UI_HANDLER_BREAK)
558 return WM_HANDLER_BREAK;
560 /* event not handled in UI, if wheel then we temporarily disable it */
564 return WM_HANDLER_CONTINUE;
567 static void wm_handler_ui_cancel(bContext *C)
569 wmWindow *win = CTX_wm_window(C);
570 ARegion *ar = CTX_wm_region(C);
571 wmEventHandler *handler, *nexthandler;
576 for (handler = ar->handlers.first; handler; handler = nexthandler) {
577 nexthandler = handler->next;
579 if (handler->ui_handle) {
582 wm_event_init_from_window(win, &event);
583 event.type = EVT_BUT_CANCEL;
584 handler->ui_handle(C, &event, handler->ui_userdata);
589 /* ********************* operators ******************* */
591 int WM_operator_poll(bContext *C, wmOperatorType *ot)
593 wmOperatorTypeMacro *otmacro;
595 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
596 wmOperatorType *ot_macro = WM_operatortype_find(otmacro->idname, 0);
598 if (0 == WM_operator_poll(C, ot_macro))
602 /* python needs operator type, so we added exception for it */
604 return ot->pyop_poll(C, ot);
611 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
612 int WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context)
614 return wm_operator_call_internal(C, ot, NULL, NULL, context, true);
617 bool WM_operator_check_ui_empty(wmOperatorType *ot)
619 if (ot->macro.first != NULL) {
620 /* for macros, check all have exec() we can call */
621 wmOperatorTypeMacro *otmacro;
622 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
623 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
624 if (otm && !WM_operator_check_ui_empty(otm)) {
631 /* Assume a ui callback will draw something. */
637 WM_operator_properties_create_ptr(&ptr, ot);
638 RNA_STRUCT_BEGIN (&ptr, prop)
640 int flag = RNA_property_flag(prop);
641 if (flag & PROP_HIDDEN) {
651 * Sets the active region for this space from the context.
653 * \see #BKE_area_find_region_active_win
655 void WM_operator_region_active_win_set(bContext *C)
657 ScrArea *sa = CTX_wm_area(C);
659 ARegion *ar = CTX_wm_region(C);
660 if (ar && ar->regiontype == RGN_TYPE_WINDOW) {
661 sa->region_active_win = BLI_findindex(&sa->regionbase, ar);
666 /* for debugging only, getting inspecting events manually is tedious */
667 void WM_event_print(const wmEvent *event)
670 const char *unknown = "UNKNOWN";
671 const char *type_id = unknown;
672 const char *val_id = unknown;
674 RNA_enum_identifier(rna_enum_event_type_items, event->type, &type_id);
675 RNA_enum_identifier(rna_enum_event_value_items, event->val, &val_id);
677 printf("wmEvent type:%d / %s, val:%d / %s,\n"
678 " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d,\n"
679 " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', keymap_idname:%s, pointer:%p\n",
680 event->type, type_id, event->val, val_id,
681 event->shift, event->ctrl, event->alt, event->oskey, event->keymodifier,
682 event->x, event->y, event->ascii,
683 BLI_str_utf8_size(event->utf8_buf), event->utf8_buf,
684 event->keymap_idname, (const void *)event);
686 #ifdef WITH_INPUT_NDOF
687 if (ISNDOF(event->type)) {
688 const wmNDOFMotionData *ndof = event->customdata;
689 if (event->type == NDOF_MOTION) {
690 printf(" ndof: rot: (%.4f %.4f %.4f), tx: (%.4f %.4f %.4f), dt: %.4f, progress: %u\n",
691 UNPACK3(ndof->rvec), UNPACK3(ndof->tvec), ndof->dt, ndof->progress);
694 /* ndof buttons printed already */
697 #endif /* WITH_INPUT_NDOF */
699 if (event->tablet_data) {
700 const wmTabletData *wmtab = event->tablet_data;
701 printf(" tablet: active: %d, pressure %.4f, tilt: (%.4f %.4f)\n",
702 wmtab->Active, wmtab->Pressure, wmtab->Xtilt, wmtab->Ytilt);
706 printf("wmEvent - NULL\n");
711 * Show the report in the info header.
713 void WM_report_banner_show(void)
715 wmWindowManager *wm = G.main->wm.first;
716 ReportList *wm_reports = &wm->reports;
717 ReportTimerInfo *rti;
719 /* After adding reports to the global list, reset the report timer. */
720 WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
722 /* Records time since last report was added */
723 wm_reports->reporttimer = WM_event_add_timer(wm, wm->winactive, TIMERREPORT, 0.05);
725 rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
726 wm_reports->reporttimer->customdata = rti;
729 bool WM_event_is_last_mousemove(const wmEvent *event)
731 while ((event = event->next)) {
732 if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
739 #ifdef WITH_INPUT_NDOF
740 void WM_ndof_deadzone_set(float deadzone)
742 GHOST_setNDOFDeadZone(deadzone);
746 static void wm_add_reports(ReportList *reports)
748 /* if the caller owns them, handle this */
749 if (reports->list.first && (reports->flag & RPT_OP_HOLD) == 0) {
750 wmWindowManager *wm = G.main->wm.first;
752 /* add reports to the global list, otherwise they are not seen */
753 BLI_movelisttolist(&wm->reports.list, &reports->list);
755 WM_report_banner_show();
759 void WM_report(ReportType type, const char *message)
763 BKE_reports_init(&reports, RPT_STORE);
764 BKE_report(&reports, type, message);
766 wm_add_reports(&reports);
768 BKE_reports_clear(&reports);
771 void WM_reportf(ReportType type, const char *format, ...)
776 ds = BLI_dynstr_new();
777 va_start(args, format);
778 BLI_dynstr_vappendf(ds, format, args);
781 char *str = BLI_dynstr_get_cstring(ds);
782 WM_report(type, str);
788 /* (caller_owns_reports == true) when called from python */
789 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, bool caller_owns_reports)
791 if (caller_owns_reports == false) { /* popup */
792 if (op->reports->list.first) {
793 /* FIXME, temp setting window, see other call to UI_popup_menu_reports for why */
794 wmWindow *win_prev = CTX_wm_window(C);
795 ScrArea *area_prev = CTX_wm_area(C);
796 ARegion *ar_prev = CTX_wm_region(C);
798 if (win_prev == NULL)
799 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
801 UI_popup_menu_reports(C, op->reports);
803 CTX_wm_window_set(C, win_prev);
804 CTX_wm_area_set(C, area_prev);
805 CTX_wm_region_set(C, ar_prev);
809 if (retval & OPERATOR_FINISHED) {
810 CLOG_STR_INFO_N(WM_LOG_OPERATORS, 1, WM_operator_pystring(C, op, false, true));
812 if (caller_owns_reports == false) {
813 BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
816 if (op->type->flag & OPTYPE_REGISTER) {
817 if (G.background == 0) { /* ends up printing these in the terminal, gets annoying */
818 /* Report the python string representation of the operator */
819 char *buf = WM_operator_pystring(C, op, false, true);
820 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
826 /* if the caller owns them, handle this */
827 wm_add_reports(op->reports);
831 * This function is mainly to check that the rules for freeing
832 * an operator are kept in sync.
834 static bool wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
836 /* Check undo flag here since undo operators are also added to the list,
837 * to support checking if the same operator is run twice. */
838 return wm && (wm->op_undo_depth == 0) && (ot->flag & (OPTYPE_REGISTER | OPTYPE_UNDO));
841 static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat, const bool store)
843 wmWindowManager *wm = CTX_wm_manager(C);
845 op->customdata = NULL;
848 WM_operator_last_properties_store(op);
851 /* we don't want to do undo pushes for operators that are being
852 * called from operators that already do an undo push. usually
853 * this will happen for python operators that call C operators */
854 if (wm->op_undo_depth == 0) {
855 if (op->type->flag & OPTYPE_UNDO)
856 ED_undo_push_op(C, op);
857 else if (op->type->flag & OPTYPE_UNDO_GROUPED)
858 ED_undo_grouped_push_op(C, op);
862 if (G.debug & G_DEBUG_WM) {
863 char *buf = WM_operator_pystring(C, op, false, true);
864 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
868 if (wm_operator_register_check(wm, op->type)) {
869 /* take ownership of reports (in case python provided own) */
870 op->reports->flag |= RPT_FREE;
872 wm_operator_register(C, op);
873 WM_operator_region_active_win_set(C);
876 WM_operator_free(op);
881 /* if repeat is true, it doesn't register again, nor does it free */
882 static int wm_operator_exec(bContext *C, wmOperator *op, const bool repeat, const bool store)
884 wmWindowManager *wm = CTX_wm_manager(C);
885 int retval = OPERATOR_CANCELLED;
887 CTX_wm_operator_poll_msg_set(C, NULL);
889 if (op == NULL || op->type == NULL)
892 if (0 == WM_operator_poll(C, op->type))
895 if (op->type->exec) {
896 if (op->type->flag & OPTYPE_UNDO) {
901 op->flag |= OP_IS_REPEAT;
903 retval = op->type->exec(C, op);
904 OPERATOR_RETVAL_CHECK(retval);
906 op->flag &= ~OP_IS_REPEAT;
909 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
914 /* XXX Disabled the repeat check to address part 2 of #31840.
915 * Carefully checked all calls to wm_operator_exec and WM_operator_repeat, don't see any reason
916 * why this was needed, but worth to note it in case something turns bad. (mont29) */
917 if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED) /* && repeat == 0 */)
918 wm_operator_reports(C, op, retval, false);
920 if (retval & OPERATOR_FINISHED) {
921 wm_operator_finished(C, op, repeat, store && wm->op_undo_depth == 0);
923 else if (repeat == 0) {
924 /* warning: modal from exec is bad practice, but avoid crashing. */
925 if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
926 WM_operator_free(op);
930 return retval | OPERATOR_HANDLED;
934 /* simply calls exec with basic checks */
935 static int wm_operator_exec_notest(bContext *C, wmOperator *op)
937 int retval = OPERATOR_CANCELLED;
939 if (op == NULL || op->type == NULL || op->type->exec == NULL)
942 retval = op->type->exec(C, op);
943 OPERATOR_RETVAL_CHECK(retval);
949 * for running operators with frozen context (modal handlers, menus)
951 * \param store Store settings for re-use.
953 * warning: do not use this within an operator to call its self! [#29537] */
954 int WM_operator_call_ex(bContext *C, wmOperator *op,
957 return wm_operator_exec(C, op, false, store);
960 int WM_operator_call(bContext *C, wmOperator *op)
962 return WM_operator_call_ex(C, op, false);
966 * This is intended to be used when an invoke operator wants to call exec on its self
967 * and is basically like running op->type->exec() directly, no poll checks no freeing,
968 * since we assume whoever called invoke will take care of that
970 int WM_operator_call_notest(bContext *C, wmOperator *op)
972 return wm_operator_exec_notest(C, op);
976 * Execute this operator again, put here so it can share above code
978 int WM_operator_repeat(bContext *C, wmOperator *op)
980 const OperatorRepeatContextHandle *context_info;
983 context_info = ED_operator_repeat_prepare_context(C, op);
984 retval = wm_operator_exec(C, op, true, true);
985 ED_operator_repeat_reset_context(C, context_info);
990 * \return true if #WM_operator_repeat can run
991 * simple check for now but may become more involved.
992 * To be sure the operator can run call `WM_operator_poll(C, op->type)` also, since this call
993 * checks if WM_operator_repeat() can run at all, not that it WILL run at any time.
995 bool WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
997 if (op->type->exec != NULL) {
1001 /* for macros, check all have exec() we can call */
1002 wmOperatorTypeMacro *otmacro;
1003 for (otmacro = op->opm->type->macro.first; otmacro; otmacro = otmacro->next) {
1004 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1005 if (otm && otm->exec == NULL) {
1015 bool WM_operator_is_repeat(const bContext *C, const wmOperator *op)
1017 /* may be in the operators list or not */
1018 wmOperator *op_prev;
1019 if (op->prev == NULL && op->next == NULL) {
1020 wmWindowManager *wm = CTX_wm_manager(C);
1021 op_prev = wm->operators.last;
1026 return (op_prev && (op->type == op_prev->type));
1029 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot,
1030 PointerRNA *properties, ReportList *reports)
1032 /* XXX operatortype names are static still. for debug */
1033 wmOperator *op = MEM_callocN(sizeof(wmOperator), ot->idname);
1035 /* XXX adding new operator could be function, only happens here now */
1037 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
1039 /* initialize properties, either copy or create */
1040 op->ptr = MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
1041 if (properties && properties->data) {
1042 op->properties = IDP_CopyProperty(properties->data);
1045 IDPropertyTemplate val = {0};
1046 op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1048 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
1050 /* initialize error reports */
1052 op->reports = reports; /* must be initialized already */
1055 op->reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
1056 BKE_reports_init(op->reports, RPT_STORE | RPT_FREE);
1059 /* recursive filling of operator macro list */
1060 if (ot->macro.first) {
1061 static wmOperator *motherop = NULL;
1062 wmOperatorTypeMacro *otmacro;
1065 /* ensure all ops are in execution order in 1 list */
1066 if (motherop == NULL) {
1072 /* if properties exist, it will contain everything needed */
1074 otmacro = ot->macro.first;
1076 RNA_STRUCT_BEGIN (properties, prop)
1079 if (otmacro == NULL)
1082 /* skip invalid properties */
1083 if (STREQ(RNA_property_identifier(prop), otmacro->idname)) {
1084 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1085 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
1086 wmOperator *opm = wm_operator_create(wm, otm, &someptr, NULL);
1088 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
1090 BLI_addtail(&motherop->macro, opm);
1091 opm->opm = motherop; /* pointer to mom, for modal() */
1093 otmacro = otmacro->next;
1099 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
1100 wmOperatorType *otm = WM_operatortype_find(otmacro->idname, 0);
1101 wmOperator *opm = wm_operator_create(wm, otm, otmacro->ptr, NULL);
1103 BLI_addtail(&motherop->macro, opm);
1104 opm->opm = motherop; /* pointer to mom, for modal() */
1112 WM_operator_properties_sanitize(op->ptr, 0);
1117 static void wm_region_mouse_co(bContext *C, wmEvent *event)
1119 ARegion *ar = CTX_wm_region(C);
1121 /* compatibility convention */
1122 event->mval[0] = event->x - ar->winrct.xmin;
1123 event->mval[1] = event->y - ar->winrct.ymin;
1126 /* these values are invalid (avoid odd behavior by relying on old mval values) */
1127 event->mval[0] = -1;
1128 event->mval[1] = -1;
1132 #if 1 /* may want to disable operator remembering previous state for testing */
1134 static bool operator_last_properties_init_impl(wmOperator *op, IDProperty *last_properties)
1136 bool changed = false;
1137 IDPropertyTemplate val = {0};
1138 IDProperty *replaceprops = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1139 PropertyRNA *iterprop;
1141 CLOG_INFO(WM_LOG_OPERATORS, 1, "loading previous properties for '%s'", op->type->idname);
1143 iterprop = RNA_struct_iterator_property(op->type->srna);
1145 RNA_PROP_BEGIN (op->ptr, itemptr, iterprop)
1147 PropertyRNA *prop = itemptr.data;
1148 if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
1149 if (!RNA_property_is_set(op->ptr, prop)) { /* don't override a setting already set */
1150 const char *identifier = RNA_property_identifier(prop);
1151 IDProperty *idp_src = IDP_GetPropertyFromGroup(last_properties, identifier);
1153 IDProperty *idp_dst = IDP_CopyProperty(idp_src);
1155 /* note - in the future this may need to be done recursively,
1156 * but for now RNA doesn't access nested operators */
1157 idp_dst->flag |= IDP_FLAG_GHOST;
1159 /* add to temporary group instead of immediate replace,
1160 * because we are iterating over this group */
1161 IDP_AddToGroup(replaceprops, idp_dst);
1169 IDP_MergeGroup(op->properties, replaceprops, true);
1170 IDP_FreeProperty(replaceprops);
1171 MEM_freeN(replaceprops);
1175 bool WM_operator_last_properties_init(wmOperator *op)
1177 bool changed = false;
1178 if (op->type->last_properties) {
1179 changed |= operator_last_properties_init_impl(op, op->type->last_properties);
1180 for (wmOperator *opm = op->macro.first; opm; opm = opm->next) {
1181 IDProperty *idp_src = IDP_GetPropertyFromGroup(op->type->last_properties, opm->idname);
1183 changed |= operator_last_properties_init_impl(opm, idp_src);
1190 bool WM_operator_last_properties_store(wmOperator *op)
1192 if (op->type->last_properties) {
1193 IDP_FreeProperty(op->type->last_properties);
1194 MEM_freeN(op->type->last_properties);
1195 op->type->last_properties = NULL;
1198 if (op->properties) {
1199 CLOG_INFO(WM_LOG_OPERATORS, 1, "storing properties for '%s'", op->type->idname);
1200 op->type->last_properties = IDP_CopyProperty(op->properties);
1203 if (op->macro.first != NULL) {
1204 for (wmOperator *opm = op->macro.first; opm; opm = opm->next) {
1205 if (opm->properties) {
1206 if (op->type->last_properties == NULL) {
1207 op->type->last_properties = IDP_New(IDP_GROUP, &(IDPropertyTemplate){0}, "wmOperatorProperties");
1209 IDProperty *idp_macro = IDP_CopyProperty(opm->properties);
1210 STRNCPY(idp_macro->name, opm->idname);
1211 IDP_ReplaceInGroup(op->type->last_properties, idp_macro);
1216 return (op->type->last_properties != NULL);
1221 bool WM_operator_last_properties_init(wmOperator *UNUSED(op))
1226 bool WM_operator_last_properties_store(wmOperator *UNUSED(op))
1234 * Also used for exec when 'event' is NULL.
1236 static int wm_operator_invoke(
1237 bContext *C, wmOperatorType *ot, wmEvent *event,
1238 PointerRNA *properties, ReportList *reports, const bool poll_only)
1240 int retval = OPERATOR_PASS_THROUGH;
1242 /* this is done because complicated setup is done to call this function that is better not duplicated */
1244 return WM_operator_poll(C, ot);
1246 if (WM_operator_poll(C, ot)) {
1247 wmWindowManager *wm = CTX_wm_manager(C);
1248 wmOperator *op = wm_operator_create(wm, ot, properties, reports); /* if reports == NULL, they'll be initialized */
1249 const bool is_nested_call = (wm->op_undo_depth != 0);
1251 if (event != NULL) {
1252 op->flag |= OP_IS_INVOKE;
1255 /* initialize setting from previous run */
1256 if (!is_nested_call) { /* not called by py script */
1257 WM_operator_last_properties_init(op);
1260 if ((event == NULL) || (event->type != MOUSEMOVE)) {
1261 CLOG_INFO(WM_LOG_HANDLERS, 2,
1262 "handle evt %d win %p op %s",
1263 event ? event->type : 0, CTX_wm_screen(C)->active_region, ot->idname);
1266 if (op->type->invoke && event) {
1267 wm_region_mouse_co(C, event);
1269 if (op->type->flag & OPTYPE_UNDO)
1270 wm->op_undo_depth++;
1272 retval = op->type->invoke(C, op, event);
1273 OPERATOR_RETVAL_CHECK(retval);
1275 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1276 wm->op_undo_depth--;
1278 else if (op->type->exec) {
1279 if (op->type->flag & OPTYPE_UNDO)
1280 wm->op_undo_depth++;
1282 retval = op->type->exec(C, op);
1283 OPERATOR_RETVAL_CHECK(retval);
1285 if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1286 wm->op_undo_depth--;
1289 /* debug, important to leave a while, should never happen */
1290 CLOG_ERROR(WM_LOG_OPERATORS, "invalid operator call '%s'", op->idname);
1293 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
1294 * currently python only uses this */
1295 if (!(retval & OPERATOR_HANDLED) && (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED))) {
1296 /* only show the report if the report list was not given in the function */
1297 wm_operator_reports(C, op, retval, (reports != NULL));
1300 if (retval & OPERATOR_HANDLED) {
1301 /* do nothing, wm_operator_exec() has been called somewhere */
1303 else if (retval & OPERATOR_FINISHED) {
1304 const bool store = !is_nested_call;
1305 wm_operator_finished(C, op, false, store);
1307 else if (retval & OPERATOR_RUNNING_MODAL) {
1308 /* take ownership of reports (in case python provided own) */
1309 op->reports->flag |= RPT_FREE;
1311 /* grab cursor during blocking modal ops (X11)
1312 * Also check for macro
1314 if (ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
1315 int bounds[4] = {-1, -1, -1, -1};
1318 if (event == NULL) {
1322 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) &&
1323 ((op->opm->flag & OP_IS_MODAL_GRAB_CURSOR) || (op->opm->type->flag & OPTYPE_GRAB_CURSOR));
1326 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) &&
1327 ((op->flag & OP_IS_MODAL_GRAB_CURSOR) || (ot->flag & OPTYPE_GRAB_CURSOR));
1330 /* exception, cont. grab in header is annoying */
1332 ARegion *ar = CTX_wm_region(C);
1333 if (ar && ar->regiontype == RGN_TYPE_HEADER) {
1339 const rcti *winrect = NULL;
1340 ARegion *ar = CTX_wm_region(C);
1341 ScrArea *sa = CTX_wm_area(C);
1343 if (ar && ar->regiontype == RGN_TYPE_WINDOW &&
1344 BLI_rcti_isect_pt_v(&ar->winrct, &event->x))
1346 winrect = &ar->winrct;
1348 else if (sa && BLI_rcti_isect_pt_v(&sa->totrct, &event->x)) {
1349 winrect = &sa->totrct;
1353 bounds[0] = winrect->xmin;
1354 bounds[1] = winrect->ymax;
1355 bounds[2] = winrect->xmax;
1356 bounds[3] = winrect->ymin;
1360 WM_cursor_grab_enable(CTX_wm_window(C), wrap, false, bounds);
1363 /* cancel UI handlers, typically tooltips that can hang around
1364 * while dragging the view or worse, that stay there permanently
1365 * after the modal operator has swallowed all events and passed
1366 * none to the UI handler */
1367 wm_handler_ui_cancel(C);
1370 WM_operator_free(op);
1378 * #WM_operator_name_call is the main accessor function
1379 * this is for python to access since its done the operator lookup
1381 * invokes operator in context
1383 static int wm_operator_call_internal(
1384 bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports,
1385 const short context, const bool poll_only)
1391 CTX_wm_operator_poll_msg_set(C, NULL);
1395 wmWindow *window = CTX_wm_window(C);
1398 case WM_OP_INVOKE_DEFAULT:
1399 case WM_OP_INVOKE_REGION_WIN:
1400 case WM_OP_INVOKE_REGION_PREVIEW:
1401 case WM_OP_INVOKE_REGION_CHANNELS:
1402 case WM_OP_INVOKE_AREA:
1403 case WM_OP_INVOKE_SCREEN:
1404 /* window is needed for invoke, cancel operator */
1405 if (window == NULL) {
1407 CTX_wm_operator_poll_msg_set(C, "Missing 'window' in context");
1412 event = window->eventstate;
1422 case WM_OP_EXEC_REGION_WIN:
1423 case WM_OP_INVOKE_REGION_WIN:
1424 case WM_OP_EXEC_REGION_CHANNELS:
1425 case WM_OP_INVOKE_REGION_CHANNELS:
1426 case WM_OP_EXEC_REGION_PREVIEW:
1427 case WM_OP_INVOKE_REGION_PREVIEW:
1429 /* forces operator to go to the region window/channels/preview, for header menus
1430 * but we stay in the same region if we are already in one
1432 ARegion *ar = CTX_wm_region(C);
1433 ScrArea *area = CTX_wm_area(C);
1434 int type = RGN_TYPE_WINDOW;
1437 case WM_OP_EXEC_REGION_CHANNELS:
1438 case WM_OP_INVOKE_REGION_CHANNELS:
1439 type = RGN_TYPE_CHANNELS;
1442 case WM_OP_EXEC_REGION_PREVIEW:
1443 case WM_OP_INVOKE_REGION_PREVIEW:
1444 type = RGN_TYPE_PREVIEW;
1447 case WM_OP_EXEC_REGION_WIN:
1448 case WM_OP_INVOKE_REGION_WIN:
1450 type = RGN_TYPE_WINDOW;
1454 if (!(ar && ar->regiontype == type) && area) {
1456 if (type == RGN_TYPE_WINDOW) {
1457 ar1 = BKE_area_find_region_active_win(area);
1460 ar1 = BKE_area_find_region_type(area, type);
1464 CTX_wm_region_set(C, ar1);
1467 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
1469 /* set region back */
1470 CTX_wm_region_set(C, ar);
1474 case WM_OP_EXEC_AREA:
1475 case WM_OP_INVOKE_AREA:
1477 /* remove region from context */
1478 ARegion *ar = CTX_wm_region(C);
1480 CTX_wm_region_set(C, NULL);
1481 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
1482 CTX_wm_region_set(C, ar);
1486 case WM_OP_EXEC_SCREEN:
1487 case WM_OP_INVOKE_SCREEN:
1489 /* remove region + area from context */
1490 ARegion *ar = CTX_wm_region(C);
1491 ScrArea *area = CTX_wm_area(C);
1493 CTX_wm_region_set(C, NULL);
1494 CTX_wm_area_set(C, NULL);
1495 retval = wm_operator_invoke(C, ot, event, properties, reports, poll_only);
1496 CTX_wm_area_set(C, area);
1497 CTX_wm_region_set(C, ar);
1501 case WM_OP_EXEC_DEFAULT:
1502 case WM_OP_INVOKE_DEFAULT:
1503 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
1511 /* invokes operator in context */
1512 int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, short context, PointerRNA *properties)
1514 BLI_assert(ot == WM_operatortype_find(ot->idname, true));
1515 return wm_operator_call_internal(C, ot, properties, NULL, context, false);
1517 int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
1519 wmOperatorType *ot = WM_operatortype_find(opstring, 0);
1521 return WM_operator_name_call_ptr(C, ot, context, properties);
1528 * Call an existent menu. The menu can be created in C or Python.
1530 void WM_menu_name_call(bContext *C, const char *menu_name, short context)
1532 wmOperatorType *ot = WM_operatortype_find("WM_OT_call_menu", false);
1534 WM_operator_properties_create_ptr(&ptr, ot);
1535 RNA_string_set(&ptr, "name", menu_name);
1536 WM_operator_name_call_ptr(C, ot, context, &ptr);
1537 WM_operator_properties_free(&ptr);
1541 * Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context.
1543 * - #wmOperatorType is used instead of operator name since python already has the operator type.
1544 * - `poll()` must be called by python before this runs.
1545 * - reports can be passed to this function (so python can report them as exceptions).
1547 int WM_operator_call_py(
1548 bContext *C, wmOperatorType *ot, short context,
1549 PointerRNA *properties, ReportList *reports, const bool is_undo)
1551 int retval = OPERATOR_CANCELLED;
1555 op = wm_operator_create(wm, ot, properties, reports);
1557 if (op->type->exec) {
1558 if (is_undo && op->type->flag & OPTYPE_UNDO)
1559 wm->op_undo_depth++;
1561 retval = op->type->exec(C, op);
1562 OPERATOR_RETVAL_CHECK(retval);
1564 if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1565 wm->op_undo_depth--;
1568 CLOG_WARN(WM_LOG_OPERATORS, "\"%s\" operator has no exec function, Python cannot call it", op->type->name);
1573 /* not especially nice using undo depth here, its used so py never
1574 * triggers undo or stores operators last used state.
1576 * we could have some more obvious way of doing this like passing a flag.
1578 wmWindowManager *wm = CTX_wm_manager(C);
1579 if (!is_undo && wm) wm->op_undo_depth++;
1581 retval = wm_operator_call_internal(C, ot, properties, reports, context, false);
1583 if (!is_undo && wm && (wm == CTX_wm_manager(C))) wm->op_undo_depth--;
1589 /* ********************* handlers *************** */
1591 /* future extra customadata free? */
1592 void wm_event_free_handler(wmEventHandler *handler)
1597 /* only set context when area/region is part of screen */
1598 static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wmEvent *event)
1600 wmWindow *win = CTX_wm_window(C);
1601 bScreen *screen = CTX_wm_screen(C);
1603 if (screen && handler->op) {
1604 if (handler->op_area == NULL)
1605 CTX_wm_area_set(C, NULL);
1609 ED_screen_areas_iter(win, screen, sa_iter) {
1610 if (sa_iter == handler->op_area) {
1617 /* when changing screen layouts with running modal handlers (like render display), this
1618 * is not an error to print */
1619 if (handler->op == NULL) {
1620 CLOG_ERROR(WM_LOG_HANDLERS, "internal error: handler (%s) has invalid area", handler->op->type->idname);
1625 wmOperator *op = handler->op ? (handler->op->opm ? handler->op->opm : handler->op) : NULL;
1626 CTX_wm_area_set(C, sa);
1628 if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) {
1629 ar = BKE_area_find_region_xy(sa, handler->op_region_type, event->x, event->y);
1631 handler->op_region = ar;
1639 for (ar = sa->regionbase.first; ar; ar = ar->next) {
1640 if (ar == handler->op_region) {
1646 /* XXX no warning print here, after full-area and back regions are remade */
1648 CTX_wm_region_set(C, ar);
1654 /* called on exit or remove area, only here call cancel callback */
1655 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
1657 wmEventHandler *handler;
1658 wmWindowManager *wm = CTX_wm_manager(C);
1660 /* C is zero on freeing database, modal handlers then already were freed */
1661 while ((handler = BLI_pophead(handlers))) {
1663 wmWindow *win = CTX_wm_window(C);
1664 if (handler->op->type->cancel) {
1665 ScrArea *area = CTX_wm_area(C);
1666 ARegion *region = CTX_wm_region(C);
1668 wm_handler_op_context(C, handler, win->eventstate);
1670 if (handler->op->type->flag & OPTYPE_UNDO)
1671 wm->op_undo_depth++;
1673 handler->op->type->cancel(C, handler->op);
1675 if (handler->op->type->flag & OPTYPE_UNDO)
1676 wm->op_undo_depth--;
1678 CTX_wm_area_set(C, area);
1679 CTX_wm_region_set(C, region);
1682 WM_cursor_grab_disable(win, NULL);
1683 WM_operator_free(handler->op);
1685 else if (handler->ui_remove) {
1686 ScrArea *area = CTX_wm_area(C);
1687 ARegion *region = CTX_wm_region(C);
1688 ARegion *menu = CTX_wm_menu(C);
1690 if (handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
1691 if (handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
1692 if (handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
1694 handler->ui_remove(C, handler->ui_userdata);
1696 CTX_wm_area_set(C, area);
1697 CTX_wm_region_set(C, region);
1698 CTX_wm_menu_set(C, menu);
1701 wm_event_free_handler(handler);
1705 /* do userdef mappings */
1706 int WM_userdef_event_map(int kmitype)
1710 return (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE;
1712 return (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE;
1714 return (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_R : EVT_TWEAK_L;
1716 return (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R;
1718 return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
1720 return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
1727 * Use so we can check if 'wmEvent.type' is released in modal operators.
1729 * An alternative would be to add a 'wmEvent.type_nokeymap'... or similar.
1731 int WM_userdef_event_type_from_keymap_type(int kmitype)
1735 return (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE;
1737 return (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE;
1739 return (U.flag & USER_LMOUSESELECT) ? LEFTMOUSE : RIGHTMOUSE;
1741 return (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE;
1749 return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE;
1751 return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELDOWNMOUSE : WHEELUPMOUSE;
1757 static int wm_eventmatch(const wmEvent *winevent, wmKeyMapItem *kmi)
1759 int kmitype = WM_userdef_event_map(kmi->type);
1761 if (kmi->flag & KMI_INACTIVE) return 0;
1763 /* the matching rules */
1764 if (kmitype == KM_TEXTINPUT)
1765 if (winevent->val == KM_PRESS) { /* prevent double clicks */
1766 /* NOT using ISTEXTINPUT anymore because (at least on Windows) some key codes above 255
1767 * could have printable ascii keys - BUG [#30479] */
1768 if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) return 1;
1771 if (kmitype != KM_ANY) {
1772 if (ELEM(kmitype, TABLET_STYLUS, TABLET_ERASER)) {
1773 const wmTabletData *wmtab = winevent->tablet_data;
1777 else if (winevent->type != LEFTMOUSE) /* tablet events can occur on hover + keypress */
1779 else if ((kmitype == TABLET_STYLUS) && (wmtab->Active != EVT_TABLET_STYLUS))
1781 else if ((kmitype == TABLET_ERASER) && (wmtab->Active != EVT_TABLET_ERASER))
1785 if (winevent->type != kmitype)
1790 if (kmi->val != KM_ANY)
1791 if (winevent->val != kmi->val) return 0;
1793 /* modifiers also check bits, so it allows modifier order */
1794 if (kmi->shift != KM_ANY)
1795 if (winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1796 if (kmi->ctrl != KM_ANY)
1797 if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1798 if (kmi->alt != KM_ANY)
1799 if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1800 if (kmi->oskey != KM_ANY)
1801 if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1803 /* only keymap entry with keymodifier is checked, means all keys without modifier get handled too. */
1804 /* that is currently needed to make overlapping events work (when you press A - G fast or so). */
1805 if (kmi->keymodifier)
1806 if (winevent->keymodifier != kmi->keymodifier) return 0;
1812 /* operator exists */
1813 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event, bool *dbl_click_disabled)
1815 /* support for modal keymap in macros */
1819 if (op->type->modalkeymap) {
1820 wmKeyMap *keymap = WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1823 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
1824 if (wm_eventmatch(event, kmi)) {
1826 event->prevtype = event->type;
1827 event->prevval = event->val;
1828 event->type = EVT_MODAL_MAP;
1829 event->val = kmi->propvalue;
1836 /* modal keymap checking returns handled events fine, but all hardcoded modal
1837 * handling typically swallows all events (OPERATOR_RUNNING_MODAL).
1838 * This bypass just disables support for double clicks in hardcoded modal handlers */
1839 if (event->val == KM_DBL_CLICK) {
1840 event->val = KM_PRESS;
1841 *dbl_click_disabled = true;
1847 * Check whether operator is allowed to run in case interface is locked,
1848 * If interface is unlocked, will always return truth.
1850 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot)
1852 wmWindowManager *wm = CTX_wm_manager(C);
1854 if (wm->is_interface_locked) {
1855 if ((ot->flag & OPTYPE_LOCK_BYPASS) == 0) {
1863 /* bad hacking event system... better restore event type for checking of KM_CLICK for example */
1864 /* XXX modal maps could use different method (ton) */
1865 static void wm_event_modalmap_end(wmEvent *event, bool dbl_click_disabled)
1867 if (event->type == EVT_MODAL_MAP) {
1868 event->type = event->prevtype;
1869 event->prevtype = 0;
1870 event->val = event->prevval;
1873 else if (dbl_click_disabled)
1874 event->val = KM_DBL_CLICK;
1878 /* Warning: this function removes a modal handler, when finished */
1879 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler,
1880 wmEvent *event, PointerRNA *properties)
1882 int retval = OPERATOR_PASS_THROUGH;
1884 /* derived, modal or blocking operator */
1886 wmOperator *op = handler->op;
1887 wmOperatorType *ot = op->type;
1889 if (!wm_operator_check_locked_interface(C, ot)) {
1890 /* Interface is locked and operator is not allowed to run,
1891 * nothing to do in this case.
1894 else if (ot->modal) {
1895 /* we set context to where modal handler came from */
1896 wmWindowManager *wm = CTX_wm_manager(C);
1897 ScrArea *area = CTX_wm_area(C);
1898 ARegion *region = CTX_wm_region(C);
1899 bool dbl_click_disabled = false;
1901 wm_handler_op_context(C, handler, event);
1902 wm_region_mouse_co(C, event);
1903 wm_event_modalkeymap(C, op, event, &dbl_click_disabled);
1905 if (ot->flag & OPTYPE_UNDO)
1906 wm->op_undo_depth++;
1908 /* warning, after this call all context data and 'event' may be freed. see check below */
1909 retval = ot->modal(C, op, event);
1910 OPERATOR_RETVAL_CHECK(retval);
1912 /* when this is _not_ the case the modal modifier may have loaded
1913 * a new blend file (demo mode does this), so we have to assume
1914 * the event, operator etc have all been freed. - campbell */
1915 if (CTX_wm_manager(C) == wm) {
1917 wm_event_modalmap_end(event, dbl_click_disabled);
1919 if (ot->flag & OPTYPE_UNDO)
1920 wm->op_undo_depth--;
1922 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
1923 wm_operator_reports(C, op, retval, false);
1926 /* not very common, but modal operators may report before finishing */
1927 if (!BLI_listbase_is_empty(&op->reports->list)) {
1928 wm_add_reports(op->reports);
1932 /* important to run 'wm_operator_finished' before NULLing the context members */
1933 if (retval & OPERATOR_FINISHED) {
1934 wm_operator_finished(C, op, false, true);
1937 else if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
1938 WM_operator_free(op);
1942 /* putting back screen context, reval can pass trough after modal failures! */
1943 if ((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1944 CTX_wm_area_set(C, area);
1945 CTX_wm_region_set(C, region);
1948 /* this special cases is for areas and regions that get removed */
1949 CTX_wm_area_set(C, NULL);
1950 CTX_wm_region_set(C, NULL);
1953 /* update manipulators during modal handlers */
1954 wm_manipulatormaps_handled_modal_update(C, event, handler);
1956 /* remove modal handler, operator itself should have been canceled and freed */
1957 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
1958 /* set cursor back to the default for the region */
1959 wmWindow *win = CTX_wm_window(C);
1960 WM_cursor_grab_disable(win, NULL);
1961 ED_region_cursor_set(win, CTX_wm_area(C), CTX_wm_region(C));
1963 BLI_remlink(handlers, handler);
1964 wm_event_free_handler(handler);
1966 /* prevent silly errors from operator users */
1967 //retval &= ~OPERATOR_PASS_THROUGH;
1972 CLOG_ERROR(WM_LOG_HANDLERS, "missing modal '%s'", op->idname);
1976 wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0);
1979 if (wm_operator_check_locked_interface(C, ot)) {
1980 retval = wm_operator_invoke(C, ot, event, properties, NULL, false);
1984 /* Finished and pass through flag as handled */
1986 /* Finished and pass through flag as handled */
1987 if (retval == (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH))
1988 return WM_HANDLER_HANDLED;
1990 /* Modal unhandled, break */
1991 if (retval == (OPERATOR_PASS_THROUGH | OPERATOR_RUNNING_MODAL))
1992 return (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
1994 if (retval & OPERATOR_PASS_THROUGH)
1995 return WM_HANDLER_CONTINUE;
1997 return WM_HANDLER_BREAK;
2000 /* fileselect handlers are only in the window queue, so it's safe to switch screens or area types */
2001 static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHandler *handler, int val)
2003 wmWindowManager *wm = CTX_wm_manager(C);
2005 int action = WM_HANDLER_CONTINUE;
2008 case EVT_FILESELECT_FULL_OPEN:
2012 /* sa can be null when window A is active, but mouse is over window B
2013 * in this case, open file select in original window A. Also don't
2014 * use global areas. */
2015 if (handler->op_area == NULL || ED_area_is_global(handler->op_area)) {
2016 bScreen *screen = CTX_wm_screen(C);
2017 sa = (ScrArea *)screen->areabase.first;
2020 sa = handler->op_area;
2024 /* ensure the first area becomes the file browser, because the second one is the small
2025 * top (info-)area which might be too small (in fullscreens we have max two areas) */
2029 ED_area_newspace(C, sa, SPACE_FILE, true); /* 'sa' is modified in-place */
2030 /* we already had a fullscreen here -> mark new space as a stacked fullscreen */
2031 sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE);
2033 else if (sa->spacetype == SPACE_FILE) {
2034 sa = ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
2037 sa = ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
2040 /* note, getting the 'sa' back from the context causes a nasty bug where the newly created
2041 * 'sa' != CTX_wm_area(C). removed the line below and set 'sa' in the 'if' above */
2042 /* sa = CTX_wm_area(C); */
2044 /* settings for filebrowser, sfile is not operator owner but sends events */
2045 sfile = (SpaceFile *)sa->spacedata.first;
2046 sfile->op = handler->op;
2048 ED_fileselect_set_params(sfile);
2050 action = WM_HANDLER_BREAK;
2054 case EVT_FILESELECT_EXEC:
2055 case EVT_FILESELECT_CANCEL:
2056 case EVT_FILESELECT_EXTERNAL_CANCEL:
2058 /* remlink now, for load file case before removing*/
2059 BLI_remlink(handlers, handler);
2061 if (val != EVT_FILESELECT_EXTERNAL_CANCEL) {
2062 ScrArea *sa = CTX_wm_area(C);
2065 ED_screen_full_prevspace(C, sa);
2067 /* user may have left fullscreen */
2069 ED_area_prevspace(C, sa);
2073 wm_handler_op_context(C, handler, CTX_wm_window(C)->eventstate);
2075 /* needed for UI_popup_menu_reports */
2077 if (val == EVT_FILESELECT_EXEC) {
2080 if (handler->op->type->flag & OPTYPE_UNDO)
2081 wm->op_undo_depth++;
2083 retval = handler->op->type->exec(C, handler->op);
2085 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2086 if (handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
2087 wm->op_undo_depth--;
2089 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
2090 if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0) {
2091 if (handler->op->type->flag & OPTYPE_UNDO)
2092 ED_undo_push_op(C, handler->op);
2093 else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED)
2094 ED_undo_grouped_push_op(C, handler->op);
2097 if (handler->op->reports->list.first) {
2099 /* FIXME, temp setting window, this is really bad!
2100 * only have because lib linking errors need to be seen by users :(
2101 * it can be removed without breaking anything but then no linking errors - campbell */
2102 wmWindow *win_prev = CTX_wm_window(C);
2103 ScrArea *area_prev = CTX_wm_area(C);
2104 ARegion *ar_prev = CTX_wm_region(C);
2106 if (win_prev == NULL)
2107 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
2109 BKE_report_print_level_set(handler->op->reports, RPT_WARNING);
2110 UI_popup_menu_reports(C, handler->op->reports);
2112 /* XXX - copied from 'wm_operator_finished()' */
2113 /* add reports to the global list, otherwise they are not seen */
2114 BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
2116 /* more hacks, since we meddle with reports, banner display doesn't happen automatic */
2117 WM_report_banner_show();
2119 CTX_wm_window_set(C, win_prev);
2120 CTX_wm_area_set(C, area_prev);
2121 CTX_wm_region_set(C, ar_prev);
2124 /* for WM_operator_pystring only, custom report handling is done above */
2125 wm_operator_reports(C, handler->op, retval, true);
2127 if (retval & OPERATOR_FINISHED) {
2128 WM_operator_last_properties_store(handler->op);
2131 if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
2132 WM_operator_free(handler->op);
2136 if (handler->op->type->cancel) {
2137 if (handler->op->type->flag & OPTYPE_UNDO)
2138 wm->op_undo_depth++;
2140 handler->op->type->cancel(C, handler->op);
2142 if (handler->op->type->flag & OPTYPE_UNDO)
2143 wm->op_undo_depth--;
2146 WM_operator_free(handler->op);
2149 CTX_wm_area_set(C, NULL);
2151 wm_event_free_handler(handler);
2153 action = WM_HANDLER_BREAK;
2161 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, const wmEvent *event)
2163 int action = WM_HANDLER_CONTINUE;
2165 if (event->type != EVT_FILESELECT)
2167 if (handler->op != (wmOperator *)event->customdata)
2170 return wm_handler_fileselect_do(C, handlers, handler, event->val);
2173 static bool handler_boundbox_test(wmEventHandler *handler, const wmEvent *event)
2175 if (handler->bbwin) {
2176 if (handler->bblocal) {
2177 rcti rect = *handler->bblocal;
2178 BLI_rcti_translate(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
2180 if (BLI_rcti_isect_pt_v(&rect, &event->x))
2182 else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(&rect, &event->prevx))
2188 if (BLI_rcti_isect_pt_v(handler->bbwin, &event->x))
2190 else if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(handler->bbwin, &event->prevx))
2199 static int wm_action_not_handled(int action)
2201 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
2204 static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
2206 const bool do_debug_handler = (G.debug & G_DEBUG_HANDLERS) &&
2207 /* comment this out to flood the console! (if you really want to test) */
2208 !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
2210 # define PRINT if (do_debug_handler) printf
2212 wmWindowManager *wm = CTX_wm_manager(C);
2213 wmEventHandler *handler, *nexthandler;
2214 int action = WM_HANDLER_CONTINUE;
2217 if (handlers == NULL) {
2221 /* modal handlers can get removed in this loop, we keep the loop this way
2223 * note: check 'handlers->first' because in rare cases the handlers can be cleared
2224 * by the event thats called, for eg:
2226 * Calling a python script which changes the area.type, see [#32232] */
2227 for (handler = handlers->first; handler && handlers->first; handler = nexthandler) {
2229 nexthandler = handler->next;
2231 /* during this loop, ui handlers for nested menus can tag multiple handlers free */
2232 if (handler->flag & WM_HANDLER_DO_FREE) {
2235 else if (handler_boundbox_test(handler, event)) { /* optional boundbox */
2236 /* in advance to avoid access to freed event on window close */
2237 always_pass = wm_event_always_pass(event);
2239 /* modal+blocking handler */
2240 if (handler->flag & WM_HANDLER_BLOCKING)
2241 action |= WM_HANDLER_BREAK;
2243 if (handler->keymap) {
2244 wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap);
2247 PRINT("%s: checking '%s' ...", __func__, keymap->idname);
2249 if (WM_keymap_poll(C, keymap)) {
2253 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2254 if (wm_eventmatch(event, kmi)) {
2256 PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
2258 /* weak, but allows interactive callback to not use rawkey */
2259 event->keymap_idname = kmi->idname;
2261 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
2262 if (action & WM_HANDLER_BREAK) {
2263 /* not always_pass here, it denotes removed handler */
2264 CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
2268 if (action & WM_HANDLER_HANDLED) {
2269 CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
2272 CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
2282 else if (handler->ui_handle) {
2283 if (!wm->is_interface_locked) {
2284 action |= wm_handler_ui_call(C, handler, event, always_pass);
2287 else if (handler->type == WM_HANDLER_FILESELECT) {
2288 if (!wm->is_interface_locked) {
2289 /* screen context changes here */
2290 action |= wm_handler_fileselect_call(C, handlers, handler, event);
2293 else if (handler->dropboxes) {
2294 if (!wm->is_interface_locked && event->type == EVT_DROP) {
2295 wmDropBox *drop = handler->dropboxes->first;
2296 for (; drop; drop = drop->next) {
2297 /* other drop custom types allowed */
2298 if (event->custom == EVT_DATA_DRAGDROP) {
2299 ListBase *lb = (ListBase *)event->customdata;
2302 for (drag = lb->first; drag; drag = drag->next) {
2303 if (drop->poll(C, drag, event)) {
2304 drop->copy(drag, drop);
2306 /* free the drags before calling operator */
2307 WM_drag_free_list(lb);
2309 event->customdata = NULL;
2312 WM_operator_name_call_ptr(C, drop->ot, drop->opcontext, drop->ptr);
2313 action |= WM_HANDLER_BREAK;
2315 /* XXX fileread case */
2316 if (CTX_wm_window(C) == NULL)
2319 /* escape from drag loop, got freed */
2327 else if (handler->manipulator_map) {
2328 ScrArea *area = CTX_wm_area(C);
2329 ARegion *region = CTX_wm_region(C);
2330 wmManipulatorMap *mmap = handler->manipulator_map;
2331 wmManipulator *mpr = wm_manipulatormap_highlight_get(mmap);
2333 if (region->manipulator_map != handler->manipulator_map) {
2334 WM_manipulatormap_tag_refresh(handler->manipulator_map);
2337 wm_manipulatormap_handler_context(C, handler);
2338 wm_region_mouse_co(C, event);
2340 /* handle manipulator highlighting */
2341 if (event->type == MOUSEMOVE && !wm_manipulatormap_modal_get(mmap)) {
2343 mpr = wm_manipulatormap_highlight_find(mmap, C, event, &part);
2344 wm_manipulatormap_highlight_set(mmap, C, mpr, part);
2346 WM_tooltip_timer_init(C, CTX_wm_window(C), region, WM_manipulatormap_tooltip_init);
2350 /* Either we operate on a single highlighted item
2351 * or groups attached to the selected manipulators.
2352 * To simplify things both cases loop over an array of items. */
2353 wmManipulatorGroup *mgroup_first;
2354 bool is_mgroup_single;
2356 if (ISMOUSE(event->type)) {
2357 /* Keep mpr set as-is, just fake single selection. */
2359 mgroup_first = mpr->parent_mgroup;
2362 mgroup_first = NULL;
2364 is_mgroup_single = true;
2367 if (WM_manipulatormap_is_any_selected(mmap)) {
2368 const ListBase *groups = WM_manipulatormap_group_list(mmap);
2369 mgroup_first = groups->first;
2372 mgroup_first = NULL;
2374 is_mgroup_single = false;
2377 /* Don't use from now on. */
2380 for (wmManipulatorGroup *mgroup = mgroup_first; mgroup; mgroup = mgroup->next) {
2381 /* get user customized keymap from default one */
2383 if ((is_mgroup_single == false) &&
2384 /* We might want to change the logic here and use some kind of manipulator edit-mode.
2385 * For now just use keymap when a selection exists. */
2386 wm_manipulatorgroup_is_any_selected(mgroup) == false)
2391 const wmKeyMap *keymap = WM_keymap_active(wm, mgroup->type->keymap);
2394 PRINT("%s: checking '%s' ...", __func__, keymap->idname);
2396 if (!keymap->poll || keymap->poll(C)) {
2398 for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
2399 if (wm_eventmatch(event, kmi)) {
2400 wmOperator *op = handler->op;
2402 PRINT("%s: item matched '%s'\n", __func__, kmi->idname);
2404 /* weak, but allows interactive callback to not use rawkey */
2405 event->keymap_idname = kmi->idname;
2407 CTX_wm_manipulator_group_set(C, mgroup);
2409 /* handler->op is called later, we want keymap op to be triggered here */
2411 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
2414 CTX_wm_manipulator_group_set(C, NULL);
2416 if (action & WM_HANDLER_BREAK) {
2417 if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2418 printf("%s: handled - and pass on! '%s'\n",
2419 __func__, kmi->idname);
2424 if (action & WM_HANDLER_HANDLED) {
2425 if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS)) {
2426 printf("%s: handled - and pass on! '%s'\n",
2427 __func__, kmi->idname);
2431 PRINT("%s: un-handled '%s'\n",
2432 __func__, kmi->idname);
2442 if (action & WM_HANDLER_BREAK) {
2446 if (is_mgroup_single) {
2452 /* restore the area */
2453 CTX_wm_area_set(C, area);
2454 CTX_wm_region_set(C, region);
2457 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
2461 /* modal, swallows all */
2462 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
2465 if (action & WM_HANDLER_BREAK) {
2467 action &= ~WM_HANDLER_BREAK;
2473 /* XXX fileread case, if the wm is freed then the handler's
2474 * will have been too so the code below need not run. */
2475 if (CTX_wm_window(C) == NULL) {
2479 /* XXX code this for all modal ops, and ensure free only happens here */
2481 /* modal ui handler can be tagged to be freed */
2482 if (BLI_findindex(handlers, handler) != -1) { /* could be freed already by regular modal ops */
2483 if (handler->flag & WM_HANDLER_DO_FREE) {
2484 BLI_remlink(handlers, handler);
2485 wm_event_free_handler(handler);
2490 if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL))
2491 wm_cursor_arrow_move(CTX_wm_window(C), event);
2498 /* this calls handlers twice - to solve (double-)click events */
2499 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
2501 int action = wm_handlers_do_intern(C, event, handlers);
2504 if (CTX_wm_window(C) == NULL)
2507 if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE, EVENT_NONE) && !ISTIMER(event->type)) {
2509 /* test for CLICK events */
2510 if (wm_action_not_handled(action)) {
2511 wmWindow *win = CTX_wm_window(C);
2513 /* eventstate stores if previous event was a KM_PRESS, in case that
2514 * wasn't handled, the KM_RELEASE will become a KM_CLICK */
2516 if (win && event->val == KM_PRESS) {
2517 win->eventstate->check_click = true;
2520 if (win && win->eventstate->prevtype == event->type) {
2522 if ((event->val == KM_RELEASE) &&
2523 (win->eventstate->prevval == KM_PRESS) &&
2524 (win->eventstate->check_click == true))
2526 if ((abs(event->x - win->eventstate->prevclickx)) <= WM_EVENT_CLICK_WIGGLE_ROOM &&
2527 (abs(event->y - win->eventstate->prevclicky)) <= WM_EVENT_CLICK_WIGGLE_ROOM)
2529 event->val = KM_CLICK;
2531 CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
2533 action |= wm_handlers_do_intern(C, event, handlers);
2535 event->val = KM_RELEASE;
2538 win->eventstate->check_click = 0;
2541 else if (event->val == KM_DBL_CLICK) {
2542 event->val = KM_PRESS;
2543 action |= wm_handlers_do_intern(C, event, handlers);
2545 /* revert value if not handled */
2546 if (wm_action_not_handled(action)) {
2547 event->val = KM_DBL_CLICK;
2553 wmWindow *win = CTX_wm_window(C);
2556 win->eventstate->check_click = 0;
2563 static int wm_event_inside_i(wmEvent *event, rcti *rect)
2565 if (wm_event_always_pass(event))
2567 if (BLI_rcti_isect_pt_v(rect, &event->x))
2572 static ScrArea *area_event_inside(bContext *C, const int xy[2])
2574 wmWindow *win = CTX_wm_window(C);
2575 bScreen *screen = CTX_wm_screen(C);
2578 ED_screen_areas_iter(win, screen, sa) {
2579 if (BLI_rcti_isect_pt_v(&sa->totrct, xy))
2586 static ARegion *region_event_inside(bContext *C, const int xy[2])
2588 bScreen *screen = CTX_wm_screen(C);
2589 ScrArea *area = CTX_wm_area(C);
2593 for (ar = area->regionbase.first; ar; ar = ar->next)
2594 if (BLI_rcti_isect_pt_v(&ar->winrct, xy))
2599 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
2602 for (; pc; pc = pc->next) {
2603 if (pc->poll == NULL || pc->poll(C)) {
2604 wmWindow *win = CTX_wm_window(C);
2605 WM_paint_cursor_tag_redraw(win, ar);
2611 /* called on mousemove, check updates for paintcursors */
2612 /* context was set on active area and region */
2613 static void wm_paintcursor_test(bContext *C, const wmEvent *event)
2615 wmWindowManager *wm = CTX_wm_manager(C);
2617 if (wm->paintcursors.first) {
2618 ARegion *ar = CTX_wm_region(C);
2621 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
2623 /* if previous position was not in current region, we have to set a temp new context */
2624 if (ar == NULL || !BLI_rcti_isect_pt_v(&ar->winrct, &event->prevx)) {
2625 ScrArea *sa = CTX_wm_area(C);
2627 CTX_wm_area_set(C, area_event_inside(C, &event->prevx));
2628 CTX_wm_region_set(C, region_event_inside(C, &event->prevx));
2630 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
2632 CTX_wm_area_set(C, sa);
2633 CTX_wm_region_set(C, ar);
2638 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
2640 bScreen *screen = WM_window_get_active_screen(win);
2642 if (BLI_listbase_is_empty(&wm->drags)) {
2646 if (event->type == MOUSEMOVE || ISKEYMODIFIER(event->type)) {
2647 screen->do_draw_drag = true;
2649 else if (event->type == ESCKEY) {
2650 WM_drag_free_list(&wm->drags);
2652 screen->do_draw_drag = true;
2654 else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
2655 event->type = EVT_DROP;
2657 /* create customdata, first free existing */
2658 if (event->customdata) {
2659 if (event->customdatafree)
2660 MEM_freeN(event->customdata);
2663 event->custom = EVT_DATA_DRAGDROP;
2664 event->customdata = &wm->drags;
2665 event->customdatafree = 1;
2667 /* clear drop icon */
2668 screen->do_draw_drag = true;
2670 /* restore cursor (disabled, see wm_dragdrop.c) */
2671 // WM_cursor_modal_restore(win);
2675 /* filter out all events of the pie that spawned the last pie unless it's a release event */
2676 static bool wm_event_pie_filter(wmWindow *win, const wmEvent *event)
2678 if (win->lock_pie_event && win->lock_pie_event == event->type) {
2679 if (event->val == KM_RELEASE) {
2680 win->lock_pie_event = EVENT_NONE;
2692 /* called in main loop */
2693 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
2694 void wm_event_do_handlers(bContext *C)
2696 wmWindowManager *wm = CTX_wm_manager(C);
2699 /* update key configuration before handling events */
2700 WM_keyconfig_update(wm);
2701 WM_manipulatorconfig_update(CTX_data_main(C));
2703 for (win = wm->windows.first; win; win = win->next) {
2704 bScreen *screen = WM_window_get_active_screen(win);
2707 /* some safty checks - these should always be set! */
2708 BLI_assert(WM_window_get_active_scene(win));
2709 BLI_assert(WM_window_get_active_screen(win));
2710 BLI_assert(WM_window_get_active_workspace(win));
2713 wm_event_free_all(win);
2715 Scene *scene = WM_window_get_active_scene(win);
2718 int is_playing_sound = BKE_sound_scene_playing(scene);
2720 if (is_playing_sound != -1) {
2721 bool is_playing_screen;
2722 CTX_wm_window_set(C, win);
2723 CTX_data_scene_set(C, scene);
2725 is_playing_screen = (ED_screen_animation_playing(wm) != NULL);
2727 if (((is_playing_sound == 1) && (is_playing_screen == 0)) ||
2728 ((is_playing_sound == 0) && (is_playing_screen == 1)))
2730 ED_screen_animation_play(C, -1, 1);
2733 if (is_playing_sound == 0) {
2734 const float time = BKE_sound_sync_scene(scene);
2735 if (isfinite(time)) {
2736 int ncfra = time * (float)FPS + 0.5f;
2737 if (ncfra != scene->r.cfra) {
2738 scene->r.cfra = ncfra;
2739 Depsgraph *depsgraph = CTX_data_depsgraph(C);
2740 ED_update_for_newframe(CTX_data_main(C), depsgraph);
2741 WM_event_add_notifier(C, NC_WINDOW, NULL);
2746 CTX_data_scene_set(C, NULL);
2747 CTX_wm_screen_set(C, NULL);
2748 CTX_wm_window_set(C, NULL);
2753 while ( (event = win->queue.first) ) {
2754 int action = WM_HANDLER_CONTINUE;
2756 /* active screen might change during handlers, update pointer */
2757 screen = WM_window_get_active_screen(win);
2759 if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2760 printf("\n%s: Handling event\n", __func__);
2761 WM_event_print(event);
2764 /* take care of pie event filter */
2765 if (wm_event_pie_filter(win, event)) {
2766 if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
2767 CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
2769 BLI_remlink(&win->queue, event);
2770 wm_event_free(event);
2774 CTX_wm_window_set(C, win);
2776 /* Clear tool-tip on mouse move. */
2777 if (screen->tool_tip && screen->tool_tip->exit_on_event) {
2778 if (ISMOUSE(event->type)) {
2779 WM_tooltip_clear(C, win);
2783 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
2784 CTX_wm_area_set(C, area_event_inside(C, &event->x));
2785 CTX_wm_region_set(C, region_event_inside(C, &event->x));
2787 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
2788 wm_window_make_drawable(wm, win);
2790 wm_region_mouse_co(C, event);
2793 /* first we do priority handlers, modal + some limited keymaps */
2794 action |= wm_handlers_do(C, event, &win->modalhandlers);
2797 if (CTX_wm_window(C) == NULL)
2800 /* check for a tooltip */
2801 if (screen == WM_window_get_active_screen(win)) {