diff --git a/scripts/modules/bpy/ops.py b/scripts/modules/bpy/ops.py
index a7a908c8b31..5246459e0bc 100644
--- a/scripts/modules/bpy/ops.py
+++ b/scripts/modules/bpy/ops.py
@@ -13,8 +13,27 @@ _op_as_string = _ops_module.as_string
 _op_get_rna_type = _ops_module.get_rna_type
 _op_get_bl_options = _ops_module.get_bl_options
 
+_op_handlers = _ops_module.handlers
+
 _ModuleType = type(_ops_module)
 
+class handler_action:
+    def __init__(self, mod, append_func, remove_func):
+        self._mod = mod
+        self._append_func = append_func
+        self._remove_func = remove_func
+
+    def append(self,cb, owner = None, args = None, poll = None):
+         #is there a way to remove self from console show?
+         self._append_func(owner=owner, op = self._mod.idname(), cb=cb, args=args, poll = poll)
+
+    def remove(self, cb = None, owner = None):
+        self._remove_func(owner=owner, op = self._mod.idname(), cb=cb, args=None, poll = None)
+
+
+def remove_handlers(owner = None, cb = None):
+	_op_handlers.remove(owner = owner, cb = cb, op = None, args = None, poll = None)
+
 
 # -----------------------------------------------------------------------------
 # Callable Operator Wrapper
@@ -26,7 +45,14 @@ class _BPyOpsSubModOp:
     eg. bpy.ops.object.somefunc
     """
 
-    __slots__ = ("_module", "_func")
+    __slots__ = ("_module", "_func", "handlers")
+
+    class _handlers:
+        def __init__(self, mod):
+            self.invoke_pre = handler_action(mod, _op_handlers.pre_invoke, _op_handlers.pre_invoke_remove)
+            self.invoke_post = handler_action(mod, _op_handlers.post_invoke, _op_handlers.post_invoke_remove)
+            self.modal = handler_action(mod, _op_handlers.modal, _op_handlers.modal_remove)
+            self.modal_end = handler_action(mod, _op_handlers.modal_end, _op_handlers.modal_end_remove)
 
     def _get_doc(self):
         idname = self.idname()
@@ -77,6 +103,7 @@ class _BPyOpsSubModOp:
     def __init__(self, module, func):
         self._module = module
         self._func = func
+        self.handlers = self._handlers(self)
 
     def poll(self, *args):
         C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args)
@@ -178,4 +205,6 @@ def __dir__():
         else:
             submodules.add(id_split[0])
 
+    submodules.add("remove_handlers")
+
     return list(submodules)
diff --git a/source/blender/blenkernel/BKE_context.hh b/source/blender/blenkernel/BKE_context.hh
index 939b5d3cd82..b00ec3fe719 100644
--- a/source/blender/blenkernel/BKE_context.hh
+++ b/source/blender/blenkernel/BKE_context.hh
@@ -69,6 +69,7 @@ struct View3D;
 struct ViewLayer;
 struct wmGizmoGroup;
 struct wmMsgBus;
+struct wmOpHandlers;
 struct wmWindow;
 struct wmWindowManager;
 struct WorkSpace;
@@ -191,6 +192,7 @@ void *CTX_wm_region_data(const bContext *C);
 ARegion *CTX_wm_menu(const bContext *C);
 wmGizmoGroup *CTX_wm_gizmo_group(const bContext *C);
 wmMsgBus *CTX_wm_message_bus(const bContext *C);
+wmOpHandlers *CTX_wm_op_handlers(const bContext *C);
 ReportList *CTX_wm_reports(const bContext *C);
 
 View3D *CTX_wm_view3d(const bContext *C);
diff --git a/source/blender/blenkernel/intern/context.cc b/source/blender/blenkernel/intern/context.cc
index 52cd21b356f..490bc23b6f0 100644
--- a/source/blender/blenkernel/intern/context.cc
+++ b/source/blender/blenkernel/intern/context.cc
@@ -763,6 +763,11 @@ wmMsgBus *CTX_wm_message_bus(const bContext *C)
   return C->wm.manager ? C->wm.manager->message_bus : nullptr;
 }
 
+struct wmOpHandlers *CTX_wm_op_handlers(const bContext *C)
+{
+  return C->wm.manager ? C->wm.manager->op_handlers : nullptr;
+}
+
 ReportList *CTX_wm_reports(const bContext *C)
 {
   if (C->wm.manager) {
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 6439ccb884e..aed24b59aa9 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -213,6 +213,8 @@ typedef struct wmWindowManager {
 
   struct wmMsgBus *message_bus;
 
+  struct wmOpHandlers *op_handlers;
+
   //#ifdef WITH_XR_OPENXR
   wmXrData xr;
   //#endif
diff --git a/source/blender/python/intern/bpy_operator.cc b/source/blender/python/intern/bpy_operator.cc
index 52b9539a3a9..2253d2805fa 100644
--- a/source/blender/python/intern/bpy_operator.cc
+++ b/source/blender/python/intern/bpy_operator.cc
@@ -36,6 +36,7 @@
 #include "RNA_prototypes.h"
 
 #include "WM_api.hh"
+#include "WM_op_handlers.h"
 #include "WM_types.hh"
 
 #include "MEM_guardedalloc.h"
@@ -461,11 +462,493 @@ static PyModuleDef bpy_ops_module = {
     /*m_free*/ nullptr,
 };
 
+static int bpy_op_handler_check(void *py_data, void* owner, void *callback)
+{
+  PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
+  PyObject *py_callback = PyTuple_GET_ITEM(py_data, 2);
+  if (owner != NULL && callback != NULL) {
+    return (py_owner == owner && py_callback == callback);
+  }
+  else if (owner != NULL)
+  {
+    return (py_owner == owner);
+  }
+  else {  //callback != NULL
+    return (py_callback == callback);
+  }
+}
+
+/*
+ * @Properties Rna Properties that has been used to set the operator
+ */
+static PyObject *bpy_op_get_operator_params(PointerRNA *properties)
+{
+  const char *arg_name = NULL;
+  PyObject *py_dict = PyDict_New();
+  PyObject *data;
+  RNA_STRUCT_BEGIN (properties, prop) {
+    arg_name = RNA_property_identifier(prop);
+    data = NULL;
+    if (STREQ(arg_name, "rna_type")) {
+      continue;
+    }
+    switch (RNA_property_type(prop)) {
+      case PROP_BOOLEAN: {
+        bool val = RNA_property_boolean_get(properties, prop);
+        // from Py Docs, Py_False and Py_truee needs to be treated just like any other object with
+        // respect to reference counts.
+        data = val ? Py_False : Py_True;
+        break;
+      }
+      case PROP_INT: {
+        const int prop_array_length = RNA_property_array_length(properties, prop);
+        if (prop_array_length == 0) {
+          int val = RNA_property_int_get(properties, prop);
+          data = PyLong_FromLong(val);
+        }
+        else {
+          int *values = (int*) MEM_callocN(sizeof(int) * prop_array_length, __func__);
+          RNA_property_int_get_array(properties, prop, values);
+          data = PyTuple_New(prop_array_length);
+          for (int i = 0; i < prop_array_length; i++) {
+            PyObject *py_val = PyLong_FromLong(*(values + i));
+            PyTuple_SET_ITEM(data, i, py_val);
+          }
+          MEM_freeN(values);
+        }
+        break;
+      }
+      case PROP_FLOAT: {
+        const int prop_array_length = RNA_property_array_length(properties, prop);
+        if (prop_array_length == 0) {
+          float val;
+          val = RNA_property_float_get(properties, prop);
+          data = PyFloat_FromDouble(val);
+        }
+        else {
+          float *values = (float*) MEM_callocN(sizeof(float) * prop_array_length, __func__);
+          RNA_property_float_get_array(properties, prop, values);
+          data = PyTuple_New(prop_array_length);
+          for (int i = 0; i < prop_array_length; i++) {
+            PyObject *py_val = PyFloat_FromDouble(*(values + i));
+            PyTuple_SET_ITEM(data, i, py_val);
+          }
+          MEM_freeN(values);
+        }
+        break;
+      }
+      case PROP_STRING: {
+        char buff[256];
+        char *value = RNA_property_string_get_alloc(properties, prop, buff, sizeof(buff), NULL);
+        data = PyUnicode_FromString(value);
+        if (value != buff) {
+          MEM_freeN(value);
+        }
+        break;
+      }
+      case PROP_ENUM: {
+        int val = RNA_property_enum_get(properties, prop);
+        data = PyLong_FromLong(val);
+        break;
+      }
+      case PROP_POINTER: {
+        data = PyUnicode_FromString("POINTER");
+        break;
+      }
+      case PROP_COLLECTION: {
+        data = PyUnicode_FromString("COLLECTION");
+        break;
+      }
+      default:
+        BLI_assert(false);
+    }
+    if (data != NULL) {
+      PyDict_SetItemString(py_dict, arg_name, data);
+    }
+  }
+  RNA_STRUCT_END;
+  return py_dict;
+}
+
+
+static bool bpy_op_callback_get_return_value(PyObject *callback, PyObject *py_ret)
+{
+  bool ret = true; // Do not interrump on error
+  if (py_ret == NULL) {
+    PyC_Err_PrintWithFunc(callback);
+  }
+  else {
+    if (py_ret == Py_None) {
+      // pass
+    }
+    else if (py_ret == Py_True) {
+      // pass
+    }
+    else if (py_ret == Py_False){
+      ret = false;
+    } else {
+      PyErr_SetString(PyExc_ValueError, "the return value must be None or boolean");
+      PyC_Err_PrintWithFunc(callback);
+    }
+    Py_DECREF(py_ret);
+  }
+  return ret;
+}
+
+
+static PyObject *bpy_op_get_callback_call(PyObject *callback,
+    bContext *C, const wmEvent *event, int *operator_ret, PyObject *params, PyObject *callback_args)
+{
+  PointerRNA ctx_ptr;
+  PointerRNA event_ptr;
+
+  PyObject *bpy_ctx;
+  PyObject *bpy_event;
+  PyObject *py_ret;
+
+  ctx_ptr = RNA_pointer_create(nullptr, &RNA_Context, C);
+  bpy_ctx = pyrna_struct_CreatePyObject(&ctx_ptr);
+
+    if (event != NULL) {
+    event_ptr = RNA_pointer_create(NULL, &RNA_Event, (void*) event);
+    bpy_event = pyrna_struct_CreatePyObject(&event_ptr);
+  }
+  else {
+    bpy_event = Py_None;
+  }
+
+  int s = (operator_ret == NULL) ? 3 : 4;
+  int c = (callback_args == Py_None) ? s : PyTuple_GET_SIZE(callback_args) + s;
+  PyObject *func_args = PyTuple_New(c);
+
+  PyTuple_SET_ITEM(func_args, 0, bpy_ctx);
+  PyTuple_SET_ITEM(func_args, 1, bpy_event);
+  PyTuple_SET_ITEM(func_args, 2, params);
+
+  if (operator_ret != NULL) {
+    PyObject *op_ret = pyrna_enum_bitfield_to_py(rna_enum_operator_return_items, *operator_ret);
+    PyTuple_SET_ITEM(func_args, 3, op_ret);
+  }
+
+  for (int i = s; i < c; i++) {
+    PyTuple_SET_ITEM(func_args, i, PyTuple_GET_ITEM(callback_args, i - s));
+  }
+
+  py_ret = PyObject_CallObject(callback, func_args);
+  Py_DECREF(func_args);
+  return py_ret;
+}
+
+
+static bool bpy_op_handler_poll(struct bContext *C,
+                               const wmEvent *event, void *py_data,
+                                PointerRNA *properties)
+{
+  bool ret = true;
+  PyGILState_STATE gilstate;  // this is because is not thread safe
+  bpy_context_set(C, &gilstate);
+  {
+    PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
+    PyObject *py_poll = PyTuple_GET_ITEM(py_data, 4);
+
+      // Properties get null on modall poll, params are not bypassed to Py poll function
+    PyObject *params = (properties == NULL) ? Py_None : bpy_op_get_operator_params(properties);
+    if (py_poll != Py_None) {
+      PyObject *py_ret = bpy_op_get_callback_call(py_poll, C, event, NULL, params, callback_args);
+
+      if (py_ret == NULL) {
+        // Error
+        PyErr_Print();
+        return false;
+      }
+      else if (py_ret == Py_True) {
+        ret = true;
+        Py_DECREF(py_ret);
+      }
+      else if (py_ret == Py_False) {
+        ret = false;
+        Py_DECREF(py_ret);
+      }
+      else {
+        ret = false;
+        Py_DECREF(py_ret);
+        PyErr_SetString(PyExc_ValueError, "the return value must be boolean");
+        PyC_Err_PrintWithFunc(py_poll);
+      }
+    }
+    // Py_DECREF(params);
+  }
+  bpy_context_clear(C, &gilstate);
+  return ret;
+}
+
+static bool bpy_op_handler_modal (bContext *C, const wmEvent *event, void *py_data, PointerRNA *properties, int operator_ret)
+{
+  // this is because is not thread safe
+  bool ret = true;
+  PyGILState_STATE gilstate;
+  bpy_context_set(C, &gilstate);
+  {
+    PyObject *callback = PyTuple_GET_ITEM(py_data, 2);
+    PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
+    PyObject *py_ret = bpy_op_get_callback_call(callback, C, event, &operator_ret, Py_None, callback_args);
+    ret = bpy_op_callback_get_return_value(callback, py_ret);
+  }
+  bpy_context_clear(C, &gilstate);
+  return ret;
+}
+
+
+static bool bpy_op_handler_invoke(bContext *C,
+                                  const wmEvent *event,
+                                  void *py_data,
+                                  PointerRNA *properties,
+                                  int operator_ret)
+{
+  bool ret = true;
+  // this is because is not thread safe
+  PyGILState_STATE gilstate;
+   bpy_context_set(C, &gilstate);
+  {
+    PyObject *callback = PyTuple_GET_ITEM(py_data, 2);
+    PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
+    PyObject *params = bpy_op_get_operator_params(properties);
+    PyObject *py_ret =  bpy_op_get_callback_call(callback, C, event, operator_ret ? &operator_ret : NULL , params, callback_args);
+    ret = bpy_op_callback_get_return_value(callback, py_ret);
+  }
+  bpy_context_clear(C, &gilstate);
+  return ret;
+}
+
+
+static PyObject *bpy_op_handler_proc(
+                                 PyObject *args,
+                                 PyObject *kw)
+{
+  const char *error_prefix = "op_handler_proc";
+
+  PyObject *py_op = NULL;
+  PyObject *py_owner = NULL;  // Object who creates the handler
+  PyObject *callback = NULL, *py_poll = NULL;
+  PyObject *callback_args = NULL;
+
+
+  if (PyTuple_GET_SIZE(args) != 0) {
+    PyErr_Format(PyExc_TypeError, "%s: only keyword arguments are supported", error_prefix);
+  }
+
+  // see https://docs.python.org/3/c-api/arg.html
+  static const char *_keywords[] = {"owner", "op", "cb", "args", "poll", NULL};
+  static _PyArg_Parser _parser = {"OOOOO|:handler_proc", _keywords, 0};
+
+  if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &py_owner, &py_op, &callback, &callback_args, &py_poll)) {
+    PyErr_SetString(
+        PyExc_TypeError, "Cannot set arguments, or types does not match");
+  }
+
+  if (callback != Py_None && !PyFunction_Check(callback)) {
+    // Callback may be none on remove
+    PyErr_Format(
+        PyExc_TypeError, "callback expects a function, found %.200s", Py_TYPE(callback)->tp_name);
+  }
+
+  if (py_poll != Py_None && !PyFunction_Check(py_poll)) {
+    // Callback may be none on remove
+    PyErr_Format(
+        PyExc_TypeError, "poll expects a function, found %.200s", Py_TYPE(callback)->tp_name);
+  }
+
+  if (py_op != Py_None  && !PyUnicode_Check(py_op)) {
+    PyErr_Format(
+        PyExc_TypeError, "op expects an astring, found %.200s", Py_TYPE(py_op)->tp_name);
+  }
+
+  if (PyErr_Occurred() != NULL) {
+    PyErr_Print();
+    return NULL;
+  }
+
+
+  PyObject *py_data = PyTuple_New(5);
+  PyTuple_SET_ITEMS(py_data,
+                    Py_INCREF_RET(py_owner),   // 0
+                    Py_INCREF_RET(py_op),      // 1
+                    Py_INCREF_RET(callback),   // 2
+                    Py_INCREF_RET(callback_args), // 3
+                    Py_INCREF_RET(py_poll));  // 4
+
+  return Py_INCREF_RET(py_data);
+}
+
+static PyObject *op_handler_append(int handler_id , PyObject *args, PyObject *kw)
+{
+  bContext *C = BPY_context_get();
+  struct wmOpHandlers *op_handlers = CTX_wm_op_handlers(C);
+
+  PyObject *py_data = bpy_op_handler_proc(args, kw);
+
+  bool (*func)(bContext * C,const wmEvent * event, void *, PointerRNA *, int) = nullptr;
+
+  switch (handler_id) {
+    case HANDLER_TYPE_PRE_INVOKE:
+    case HANDLER_TYPE_POST_INVOKE:
+      func = bpy_op_handler_invoke;
+      break;
+    case HANDLER_TYPE_MODAL:
+    case HANDLER_TYPE_MODAL_END:
+      func = bpy_op_handler_modal;
+      break;
+  }
+
+  if (py_data != NULL) {
+    PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
+    PyObject *py_op = PyTuple_GET_ITEM(py_data, 1);
+    PyObject *py_callback = PyTuple_GET_ITEM(py_data, 2);
+    PyObject *py_poll = PyTuple_GET_ITEM(py_data, 4);
+    if (py_op == Py_None) {
+      PyErr_Format(PyExc_TypeError, "missing operator");
+    }
+    else if (py_callback == Py_None) {
+      PyErr_Format(PyExc_TypeError, "callback expects a function");
+    } else {
+      WM_op_handlers_append(op_handlers,
+                            handler_id,
+                            py_owner,
+                            PyUnicode_AsUTF8(py_op),
+                            func,
+                            bpy_op_handler_check,
+                            py_poll == Py_None ? NULL : bpy_op_handler_poll,
+                            py_data);
+    }
+  }
+
+  if (PyErr_Occurred() != NULL) {
+    PyErr_Print();
+  }
+  Py_RETURN_NONE;
+}
+
+static PyObject *op_handler_remove(int handler_id, PyObject *args, PyObject *kw)
+{
+  bContext *C = BPY_context_get();
+  struct wmOpHandlers *op_handlers = CTX_wm_op_handlers(C);
+
+  PyObject *py_data = bpy_op_handler_proc(args, kw);
+
+  if (py_data != NULL) {
+    PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
+    PyObject *py_op = PyTuple_GET_ITEM(py_data, 1);
+    PyObject *py_cb = PyTuple_GET_ITEM(py_data, 2);
+    if (py_owner == Py_None && py_cb == Py_None) {
+      PyErr_Format(PyExc_TypeError, "missing owner or callback");
+    } else {
+      if (WM_op_handlers_remove(op_handlers,
+                                handler_id,
+                                (py_op == Py_None ? NULL : PyUnicode_AsUTF8(py_op)),
+                                (py_cb == Py_None ? NULL : py_cb),
+                                (py_owner == Py_None ? NULL : py_owner)) == 0) {
+        PyErr_Format(PyExc_NameError, "data not found on %s", PyUnicode_AsUTF8(py_op));
+      }
+    }
+  }
+
+  if (PyErr_Occurred() != NULL) {
+    PyErr_Print();
+  }
+
+  Py_RETURN_NONE;
+}
+
+static PyObject *op_handler_append_pre_invoke(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_append(HANDLER_TYPE_PRE_INVOKE, args, kw);
+}
+
+static PyObject *op_handler_append_post_invoke(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_append(HANDLER_TYPE_POST_INVOKE, args, kw);
+}
+
+static PyObject *op_handler_append_modal(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_append(HANDLER_TYPE_MODAL, args, kw);
+}
+
+static PyObject *op_handler_append_modal_end(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_append(HANDLER_TYPE_MODAL_END, args, kw);
+}
+
+
+
+static PyObject *op_handler_remove_pre_invoke(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_remove(HANDLER_TYPE_PRE_INVOKE, args, kw);
+}
+
+static PyObject *op_handler_remove_post_invoke(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_remove(HANDLER_TYPE_POST_INVOKE, args, kw);
+}
+
+static PyObject *op_handler_remove_modal(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_remove(HANDLER_TYPE_MODAL, args, kw);
+}
+
+static PyObject *op_handler_remove_modal_end(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_remove(HANDLER_TYPE_MODAL_END, args, kw);
+}
+
+
+static PyObject *op_handlers_remove(PyObject *self, PyObject *args, PyObject *kw)
+{
+  return op_handler_remove(HANDLER_TYPE_ALL, args, kw);
+}
+
+
+static struct PyMethodDef bpy_ops_handlers_methods[] = {
+    {"pre_invoke", (PyCFunction)op_handler_append_pre_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"post_invoke", (PyCFunction)op_handler_append_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"modal", (PyCFunction)op_handler_append_modal, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"modal_end", (PyCFunction)op_handler_append_modal_end, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"pre_invoke_remove", (PyCFunction)op_handler_remove_pre_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"post_invoke_remove", (PyCFunction)op_handler_remove_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"modal_remove",(PyCFunction)op_handler_remove_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"modal_end_remove", (PyCFunction)op_handler_remove_modal_end, METH_VARARGS | METH_KEYWORDS, NULL},
+    {"remove", (PyCFunction)op_handlers_remove,  METH_VARARGS | METH_KEYWORDS,  NULL},
+    {NULL, NULL, 0, NULL},
+};
+
+static struct PyModuleDef bpy_ops_handlers = {
+    PyModuleDef_HEAD_INIT,
+    "_bpy.ops.handlers",
+    NULL,
+    -1, /* multiple "initialization" just copies the module dict. */
+    bpy_ops_handlers_methods,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};
+
+
+
 PyObject *BPY_operator_module()
 {
   PyObject *submodule;
 
   submodule = PyModule_Create(&bpy_ops_module);
 
+  PyObject *handlers = PyModule_Create(&bpy_ops_handlers);
+
+  Py_INCREF(handlers);
+  if (PyModule_AddObject(submodule, "handlers", handlers) < 0) {
+    Py_DECREF(submodule);
+    Py_DECREF(handlers);
+    return NULL;
+  }
+
   return submodule;
 }
diff --git a/source/blender/python/intern/bpy_rna.cc b/source/blender/python/intern/bpy_rna.cc
index 521ca5df199..7ca37b8d3aa 100644
--- a/source/blender/python/intern/bpy_rna.cc
+++ b/source/blender/python/intern/bpy_rna.cc
@@ -1303,6 +1303,25 @@ static int pyrna_prop_to_enum_bitfield(
   return ret;
 }
 
+PyObject *pyrna_enum_bitfield_to_py(const EnumPropertyItem *items, int value)
+{
+  PyObject *ret = PySet_New(NULL);
+  const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1];
+
+  if (RNA_enum_bitflag_identifiers(items, value, identifier)) {
+    PyObject *item;
+    int index;
+    for (index = 0; identifier[index]; index++) {
+      item = PyUnicode_FromString(identifier[index]);
+      PySet_Add(ret, item);
+      Py_DECREF(item);
+    }
+  }
+
+  return ret;
+}
+
+
 static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val)
 {
   PyObject *item, *ret = nullptr;
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 661c7062462..2e57405b47a 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -189,6 +189,8 @@ PyObject *pyrna_id_CreatePyObject(struct ID *id);
 bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id);
 bool pyrna_id_CheckPyObject(PyObject *obj);
 
+PyObject *pyrna_enum_bitfield_to_py(const struct EnumPropertyItem *items, int value);
+
 /* operators also need this to set args */
 int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, bool all_args, const char *error_prefix);
 PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop);
diff --git a/source/blender/tornavis/patches/MB_0011.h b/source/blender/tornavis/patches/MB_0011.h
new file mode 100644
index 00000000000..8ac91a5f12f
--- /dev/null
+++ b/source/blender/tornavis/patches/MB_0011.h
@@ -0,0 +1 @@
+/* Empty file */
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index ae3cd30b074..8da0a2ecd3b 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -71,10 +71,12 @@ set(SRC
   message_bus/intern/wm_message_bus.cc
   message_bus/intern/wm_message_bus_rna.cc
   message_bus/intern/wm_message_bus_static.cc
+  op_handlers/intern/wm_op_handlers.cc
 
   WM_api.hh
   WM_keymap.hh
   WM_message.hh
+  WM_op_handlers.h
   WM_toolsystem.h
   WM_types.hh
   wm.hh
@@ -94,6 +96,8 @@ set(SRC
   gizmo/intern/wm_gizmo_intern.h
   message_bus/intern/wm_message_bus_intern.hh
   message_bus/wm_message_bus.hh
+  op_handlers/wm_op_handlers.h
+  op_handlers/intern/wm_op_handlers_intern.h
 )
 
 set(LIB
diff --git a/source/blender/windowmanager/WM_op_handlers.h b/source/blender/windowmanager/WM_op_handlers.h
new file mode 100644
index 00000000000..45c74f0fcaa
--- /dev/null
+++ b/source/blender/windowmanager/WM_op_handlers.h
@@ -0,0 +1,25 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+
+#pragma once
+
+struct wmOpHandlers;
+
+#include "op_handlers/wm_op_handlers.h"
diff --git a/source/blender/windowmanager/intern/wm.cc b/source/blender/windowmanager/intern/wm.cc
index a49076bdfef..d5010e7ddc5 100644
--- a/source/blender/windowmanager/intern/wm.cc
+++ b/source/blender/windowmanager/intern/wm.cc
@@ -41,6 +41,7 @@
 
 #include "WM_api.hh"
 #include "WM_message.hh"
+#include "WM_op_handlers.h"
 #include "WM_types.hh"
 #include "wm.hh"
 #include "wm_draw.hh"
@@ -217,6 +218,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
   wm->undo_stack = nullptr;
 
   wm->message_bus = nullptr;
+  wm->op_handlers = nullptr;
 
   wm->xr.runtime = nullptr;
 
@@ -474,6 +476,10 @@ void WM_check(bContext *C)
     wm->message_bus = WM_msgbus_create();
   }
 
+  if (wm->op_handlers == NULL) {
+    wm->op_handlers = WM_op_handlers_create();
+  }
+
   if (!G.background) {
     /* Case: file-read. */
     if ((wm->init_flag & WM_INIT_FLAG_WINDOW) == 0) {
@@ -574,6 +580,11 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
     WM_msgbus_destroy(wm->message_bus);
   }
 
+  if (wm->op_handlers != nullptr) {
+    WM_op_handlers_destroy(wm->op_handlers);
+  }
+
+
 #ifdef WITH_PYTHON
   BPY_callback_wm_free(wm);
 #endif
diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc
index b809a87e627..ef097873b8c 100644
--- a/source/blender/windowmanager/intern/wm_event_system.cc
+++ b/source/blender/windowmanager/intern/wm_event_system.cc
@@ -66,6 +66,7 @@
 
 #include "WM_api.hh"
 #include "WM_message.hh"
+#include "WM_op_handlers.h"
 #include "WM_toolsystem.h"
 #include "WM_types.hh"
 
@@ -1510,6 +1511,12 @@ static int wm_operator_invoke(bContext *C,
     return WM_operator_poll(C, ot);
   }
 
+  // Ensure any change is processed by poll
+  if (WM_op_handlers_operator_pre_invoke(C, event, CTX_wm_op_handlers(C), ot, properties) ==
+      false) {
+    return OPERATOR_FINISHED;
+  }
+
   if (WM_operator_poll(C, ot)) {
     wmWindowManager *wm = CTX_wm_manager(C);
     const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
@@ -1550,6 +1557,8 @@ static int wm_operator_invoke(bContext *C,
       retval = op->type->invoke(C, op, &event_temp);
       OPERATOR_RETVAL_CHECK(retval);
 
+      WM_op_handlers_operator_post_invoke(C, event, CTX_wm_op_handlers(C), ot, op->ptr, retval);
+
       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
         wm->op_undo_depth--;
       }
@@ -1562,6 +1571,8 @@ static int wm_operator_invoke(bContext *C,
       retval = op->type->exec(C, op);
       OPERATOR_RETVAL_CHECK(retval);
 
+      WM_op_handlers_operator_post_invoke(C, event, CTX_wm_op_handlers(C), ot, op->ptr, retval);
+
       if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
         wm->op_undo_depth--;
       }
@@ -2476,9 +2487,20 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
         wm->op_undo_depth++;
       }
 
-      /* Warning, after this call all context data and 'event' may be freed. see check below. */
-      retval = ot->modal(C, op, event);
-      OPERATOR_RETVAL_CHECK(retval);
+      if ( (WM_get_op_handlers(CTX_wm_op_handlers(C), ot->idname) != NULL) &&  !ot->poll(C)) {
+        // Py Handler, changing poll conditions
+        retval = OPERATOR_CANCELLED;
+      }
+      else {
+
+        /* Warning, after this call all context data and 'event' may be freed. see check below. */
+        retval = ot->modal(C, op, event);
+        OPERATOR_RETVAL_CHECK(retval);
+
+        if (WM_op_handlers_operator_modal(C, event, CTX_wm_op_handlers(C), ot, retval) == false) {
+          retval = OPERATOR_CANCELLED;
+        }
+      }
 
       if (ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
         wm->op_undo_depth--;
@@ -2492,7 +2514,10 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
         wm_event_modalkeymap_end(event, &event_backup);
 
         if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
-          wm_operator_reports(C, op, retval, false);
+
+          WM_op_handlers_operator_modal_end(C, nullptr, CTX_wm_op_handlers(C), ot, retval);
+
+            wm_operator_reports(C, op, retval, false);
 
           wmOperator *op_test = handler->op->opm ? handler->op->opm : handler->op;
           if (op_test->type->modalkeymap) {
diff --git a/source/blender/windowmanager/op_handlers/intern/wm_op_handlers.cc b/source/blender/windowmanager/op_handlers/intern/wm_op_handlers.cc
new file mode 100644
index 00000000000..e46acfaa821
--- /dev/null
+++ b/source/blender/windowmanager/op_handlers/intern/wm_op_handlers.cc
@@ -0,0 +1,246 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+
+#include <string.h>
+
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+
+#include "WM_types.hh"
+
+#include "op_handlers/intern/wm_op_handlers_intern.h"
+#include "op_handlers/wm_op_handlers.h"
+
+/* -------------------------------------------------------------------------- */
+/** \name Public API
+ * \{ */
+
+
+
+struct wmOpHandlers *WM_op_handlers_create(void)
+{
+
+  struct wmOpHandlers *op_handlers = (wmOpHandlers*) MEM_callocN(sizeof(*op_handlers), __func__);
+  return op_handlers;
+}
+
+void WM_op_handlers_destroy(struct wmOpHandlers *op_handlers)
+{
+  LISTBASE_FOREACH (wmOpHandlerData *, opHandlers, &op_handlers->handlers ) {
+    BLI_freelistN(&opHandlers->pre_invoke);
+    BLI_freelistN(&opHandlers->post_invoke);
+    BLI_freelistN(&opHandlers->modal);
+    BLI_freelistN(&opHandlers->modal_end);
+  }
+  BLI_freelistN(&op_handlers->handlers);
+  MEM_freeN(op_handlers);
+  op_handlers = NULL;
+}
+
+ListBase *WM_op_handlers_get_handler_list(wmOpHandlerData *opHandlers, int id)
+{
+  ListBase *list = NULL;
+  switch (id) {
+    case HANDLER_TYPE_PRE_INVOKE:
+      list = &opHandlers->pre_invoke;
+      break;
+    case HANDLER_TYPE_POST_INVOKE:
+      list = &opHandlers->post_invoke;
+      break;
+    case HANDLER_TYPE_MODAL:
+      list = &opHandlers->modal;
+      break;
+    case HANDLER_TYPE_MODAL_END:
+      list = &opHandlers->modal_end;
+      break;
+    default:
+      BLI_assert(false);
+  }
+  return list;
+}
+
+wmOpHandlerData *WM_get_op_handlers(struct wmOpHandlers *op_handlers, const char *op_name)
+{
+  return (wmOpHandlerData*) BLI_findstring(&op_handlers->handlers, op_name, offsetof(wmOpHandlerData, id_name));
+}
+
+
+void WM_op_handlers_append(struct wmOpHandlers *op_handlers,
+                           int id,
+                           void *py_handle,
+                           const char *op_name,
+                           bool (*cb)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties, int),
+                           int (*check)(void *, void*, void*),
+                           bool (*poll)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties),
+                           void *py_data)
+{
+  wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, op_name);
+  wmHandlerData *data = (wmHandlerData *)MEM_mallocN(sizeof(wmHandlerData), "wmHandlerData");
+  
+  if (opHandlers == NULL) {
+    // Create
+    opHandlers = (wmOpHandlerData *) MEM_callocN(sizeof(wmOpHandlerData), "wmOpHandlerData");
+    BLI_strncpy(opHandlers->id_name, op_name, OP_MAX_TYPENAME);
+    BLI_addtail(&op_handlers->handlers, opHandlers);
+  }
+
+  data->py_handle = py_handle;
+  data->id_name = opHandlers->id_name;
+  data->cb = cb;
+  data->check = check;
+  data->poll = poll;
+  data->py_data = py_data;
+
+  ListBase *list = WM_op_handlers_get_handler_list(opHandlers, id);
+  BLI_addtail(list, data);
+}
+
+int WM_op_handlers_remove_all(struct wmOpHandlers *op_handlers, void *cb, void *owner)
+{
+  int ret = 0;
+  LISTBASE_FOREACH (wmOpHandlerData *, opHandlers, &op_handlers->handlers) {
+    ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_PRE_INVOKE, opHandlers->id_name, cb, owner);
+    ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_POST_INVOKE, opHandlers->id_name, cb, owner);
+    ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_MODAL, opHandlers->id_name, cb, owner);
+    ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_MODAL_END, opHandlers->id_name, cb, owner);
+  }
+  return ret;
+}
+
+int WM_op_handlers_remove(
+    struct wmOpHandlers *op_handlers, int id, const char *op_name, void *cb, void *owner)
+{
+  int ret = 0;
+  if (id == HANDLER_TYPE_ALL) {
+    ret = WM_op_handlers_remove_all(op_handlers, cb, owner);
+  }
+  else {
+    wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, op_name);
+
+    if (opHandlers != NULL) {
+      ListBase *list = WM_op_handlers_get_handler_list(opHandlers, id);
+      wmHandlerData *next;
+      for (wmHandlerData *data = (wmHandlerData*) list->first; data; data = next) {
+        next = data->next;
+        if (data->check(data->py_data, owner, cb)) {
+          BLI_freelinkN(list, data);
+          ret++;
+        }
+      }
+    }
+  }
+  return ret;
+}
+
+
+bool WM_op_handlers_operator_exec(struct bContext *C,
+                                  const wmEvent *event,
+                                  ListBase *list,
+                                  struct wmOperatorType *ot,
+                                  struct PointerRNA *properties,
+                                  int retval)
+{
+  bool ret = true;
+  LISTBASE_FOREACH (wmHandlerData *, data, list) {
+    if (data->poll == nullptr || data->poll(C, event, data->py_data, properties)) {
+      ret = ret && data->cb(C, event, data->py_data, properties, retval);
+    }
+  }
+  return ret;
+}
+
+
+// Previous to invoke
+bool WM_op_handlers_operator_pre_invoke(struct bContext *C,
+                                        const wmEvent *event,
+                                        struct wmOpHandlers *op_handlers,
+                                        struct wmOperatorType *ot,
+                                        PointerRNA *properties)
+{
+  bool ret = true;
+  if (op_handlers != NULL) {
+    wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
+    if (opHandlers != NULL) {
+      ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_PRE_INVOKE);
+      ret = ret && WM_op_handlers_operator_exec(C, event, list, ot, properties, 0);
+    }
+  }
+  return ret;
+}
+
+
+// Post To invoke
+void WM_op_handlers_operator_post_invoke(struct bContext *C,
+                                         const wmEvent *event,
+                                         struct wmOpHandlers *op_handlers,
+                                     struct wmOperatorType *ot,
+                                     struct PointerRNA *properties,
+                                     int retval)
+{
+  if (op_handlers != NULL) {
+    wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
+    if (opHandlers != NULL) {
+      ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_POST_INVOKE);
+      WM_op_handlers_operator_exec(C, event, list, ot, properties, retval);
+    }
+  }
+}
+
+bool WM_op_handlers_operator_modal(struct bContext *C,
+                                   const wmEvent *event,
+                                   struct wmOpHandlers *op_handlers,
+                                   struct wmOperatorType *ot,
+                                   int retval
+                                   )
+{
+  bool ret = true;
+  if (op_handlers != NULL) {
+    wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
+    if (opHandlers != NULL) {
+      ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_MODAL);
+      ret = ret && WM_op_handlers_operator_exec(C, event, list, ot, NULL, retval);
+    }
+  }
+  return ret;
+}
+
+void WM_op_handlers_operator_modal_end(struct bContext *C,
+                                   const wmEvent *event,
+                                   struct wmOpHandlers *op_handlers,
+                                   struct wmOperatorType *ot,
+                                   int retval)
+{
+  if (op_handlers != NULL) {
+    wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
+    if (opHandlers != NULL) {
+      ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_MODAL_END);
+      WM_op_handlers_operator_exec(C, event, list, ot, NULL, retval);
+    }
+  }
+}
+
+
+/** \} */
diff --git a/source/blender/windowmanager/op_handlers/intern/wm_op_handlers_intern.h b/source/blender/windowmanager/op_handlers/intern/wm_op_handlers_intern.h
new file mode 100644
index 00000000000..8532fa482b5
--- /dev/null
+++ b/source/blender/windowmanager/op_handlers/intern/wm_op_handlers_intern.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+
+#pragma once
+
+#include "../wm_op_handlers.h"
+
+
+typedef struct wmHandlerData {
+  struct wmHandlerData *next, *prev;
+  char *id_name; // Pointer to wmOpHandlerData.id_name
+  void *py_handle;
+  void *py_data;
+  bool (*cb)(struct bContext *, const wmEvent *event, void *, PointerRNA *properties, int);  // callback
+  int (*check)(void *, void*, void*);
+  bool (*poll)(struct bContext *, const wmEvent *event, void *, PointerRNA *properties);
+} wmHandlerData;
+
+
diff --git a/source/blender/windowmanager/op_handlers/wm_op_handlers.h b/source/blender/windowmanager/op_handlers/wm_op_handlers.h
new file mode 100644
index 00000000000..24bf24c4566
--- /dev/null
+++ b/source/blender/windowmanager/op_handlers/wm_op_handlers.h
@@ -0,0 +1,101 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+
+#pragma once
+
+
+#include <stdio.h>
+#include "RNA_types.hh"
+
+#define HANDLER_TYPE_ALL 0
+#define HANDLER_TYPE_PRE_INVOKE 1
+#define HANDLER_TYPE_POST_INVOKE 2
+#define HANDLER_TYPE_MODAL 3
+#define HANDLER_TYPE_MODAL_END 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct wmOpHandlers {
+  /** Handlers in order of being added. */
+  ListBase handlers;
+};
+
+
+typedef struct wmOpHandlerData {
+  struct wmOpHandlerData *next, *prev;
+  char id_name[OP_MAX_TYPENAME];
+  /** Handlers in order of being added. */
+  ListBase pre_invoke;
+  ListBase post_invoke;
+  ListBase modal;
+  ListBase modal_end;
+} wmOpHandlerData;
+
+
+struct wmOpHandlers *WM_op_handlers_create(void);
+void WM_op_handlers_destroy(struct wmOpHandlers *opHandlers);
+
+wmOpHandlerData *WM_get_op_handlers(struct wmOpHandlers *op_handlers, const char *op_name);
+
+void WM_op_handlers_append(struct wmOpHandlers *op_handlers, int id,
+                           void *handle,
+                           const char *op_name,
+                           bool (*cb)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties, int),
+                           int (*check)(void *, void*, void *),
+                           bool (*poll)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties),
+                           void *py_data);
+
+int WM_op_handlers_remove(struct wmOpHandlers *op_handlers,
+                                 int id,
+                                 const char *op_name,
+                                 void *cb,
+                                 void *owner);
+
+
+bool WM_op_handlers_operator_pre_invoke(struct bContext *C,
+                                            const struct wmEvent *event,
+                                            struct wmOpHandlers *op_handlers,
+                                            struct wmOperatorType *ot,
+                                            PointerRNA *properties);
+void WM_op_handlers_operator_post_invoke(struct bContext *C,
+                                         const struct wmEvent *event,
+                                         struct wmOpHandlers *op_handlers,
+                                         struct wmOperatorType *ot,
+                                         PointerRNA *properties, int retval);
+
+
+bool WM_op_handlers_operator_modal(struct bContext *C,
+                                   const struct wmEvent *event,
+                                   struct wmOpHandlers *op_handlers,
+                                   struct wmOperatorType *ot, int retval);
+
+
+void WM_op_handlers_operator_modal_end(struct bContext *C,
+                                   const struct wmEvent *event,
+                                   struct wmOpHandlers *op_handlers,
+                                   struct wmOperatorType *ot,
+                                   int retval);
+
+
+#ifdef __cplusplus
+}
+#endif
