diff --git a/source/blender/makesrna/intern/rna_wm.cc b/source/blender/makesrna/intern/rna_wm.cc index fdfa9e95831..d9a773591a0 100644 --- a/source/blender/makesrna/intern/rna_wm.cc +++ b/source/blender/makesrna/intern/rna_wm.cc @@ -1338,13 +1338,18 @@ static int rna_operator_exec_cb(bContext *C, wmOperator *op) RNA_parameter_list_create(&list, &opr, func); RNA_parameter_set_lookup(&list, "context", &C); - op->type->rna_ext.call(C, &opr, func, &list); + const bool has_error = op->type->rna_ext.call(C, &opr, func, &list) == -1; RNA_parameter_get_lookup(&list, "result", &ret); result = *(int *)ret; RNA_parameter_list_free(&list); + if (UNLIKELY(has_error)) { + /* A modal handler may have been added, ensure this is removed, see: #113479. */ + WM_event_remove_modal_handler_all(op, false); + } + return result; } @@ -1388,13 +1393,18 @@ static int rna_operator_invoke_cb(bContext *C, wmOperator *op, const wmEvent *ev RNA_parameter_list_create(&list, &opr, func); RNA_parameter_set_lookup(&list, "context", &C); RNA_parameter_set_lookup(&list, "event", &event); - op->type->rna_ext.call(C, &opr, func, &list); + const bool has_error = op->type->rna_ext.call(C, &opr, func, &list) == -1; RNA_parameter_get_lookup(&list, "result", &ret); result = *(int *)ret; RNA_parameter_list_free(&list); + if (UNLIKELY(has_error)) { + /* A modal handler may have been added, ensure this is removed, see: #113479. */ + WM_event_remove_modal_handler_all(op, false); + } + return result; } diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index de219642cb5..b7953353f1b 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -545,6 +545,11 @@ wmEventHandler_Op *WM_event_add_modal_handler_ex(wmWindow *win, ARegion *region, wmOperator *op) ATTR_NONNULL(1, 4); wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op) ATTR_NONNULL(1, 2); +void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, bool postpone) + ATTR_NONNULL(1, 2); + +void WM_event_remove_modal_handler_all(const wmOperator *op, bool postpone) ATTR_NONNULL(1); + /** * Modal handlers store a pointer to an area which might be freed while the handler runs. * Use this function to NULL all handler pointers to \a old_area. diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 47e45392744..89b7769fc0f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -4468,6 +4468,35 @@ wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op) return WM_event_add_modal_handler_ex(win, area, region, op); } +void WM_event_remove_model_handler(ListBase *handlers, const wmOperator *op, const bool postpone) +{ + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { + if (handler_base->type == WM_HANDLER_TYPE_OP) { + wmEventHandler_Op *handler = (wmEventHandler_Op *)handler_base; + if ((handler->op == op) || (op->opm && (handler->op == op->opm))) { + /* Handlers will be freed in #wm_handlers_do(). */ + if (postpone) { + handler->head.flag |= WM_HANDLER_DO_FREE; + } + else { + BLI_remlink(handlers, handler); + wm_event_free_handler(&handler->head); + } + break; + } + } + } +} + +void WM_event_remove_modal_handler_all(const wmOperator *op, const bool postpone) +{ + Main *bmain = G_MAIN; + wmWindowManager *wm = static_cast(bmain->wm.first); + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + WM_event_remove_model_handler(&win->modalhandlers, op, postpone); + } +} + void WM_event_modal_handler_area_replace(wmWindow *win, const ScrArea *old_area, ScrArea *new_area) { LISTBASE_FOREACH (wmEventHandler *, handler_base, &win->modalhandlers) {