Fix T63694: Crash using tool gizmos with multiple windows
authorCampbell Barton <ideasman42@gmail.com>
Fri, 28 Jun 2019 08:10:43 +0000 (18:10 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 28 Jun 2019 08:22:53 +0000 (18:22 +1000)
Gizmo group types now store a user count so they aren't unlinked
while other tools are using them.

The tool system now works with multiple windows.

source/blender/editors/util/gizmo_utils.c
source/blender/windowmanager/gizmo/WM_gizmo_api.h
source/blender/windowmanager/gizmo/WM_gizmo_types.h
source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
source/blender/windowmanager/gizmo/intern/wm_gizmo_intern.h
source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c

index d330019c8160520518ea838cd9b6728b7e120080..b856c8314244d7b95b8578ee667c82828020e10b 100644 (file)
@@ -59,7 +59,12 @@ bool ED_gizmo_poll_or_unlink_delayed_from_tool_ex(const bContext *C,
 {
   bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C);
   if ((tref_rt == NULL) || !STREQ(gzgt_idname, tref_rt->gizmo_group)) {
-    WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
+    ScrArea *sa = CTX_wm_area(C);
+    wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(&gzgt->gzmap_params);
+    WM_gizmo_group_unlink_delayed_ptr_from_space(gzgt, gzmap_type, sa);
+    if (gzgt->users == 0) {
+      WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
+    }
     return false;
   }
   return true;
index 293b0cd91dc427d08f9c9eac01e8386253c80451..5896424d5fe1a08437f04522293d0896d5772dcd 100644 (file)
@@ -34,6 +34,7 @@ struct GHashIterator;
 struct IDProperty;
 struct Main;
 struct PropertyRNA;
+struct ScrArea;
 struct bToolRef;
 struct wmGizmo;
 struct wmGizmoGroup;
@@ -166,6 +167,8 @@ void WM_gizmoconfig_update_tag_group_type_remove(struct wmGizmoMapType *gzmap_ty
                                                  struct wmGizmoGroupType *gzgt);
 void WM_gizmoconfig_update(struct Main *bmain);
 
+void WM_gizmoconfig_update_tag_group_remove(struct wmGizmoMap *gzmap);
+
 /* wm_maniulator_target_props.c */
 struct wmGizmoProperty *WM_gizmo_target_property_array(struct wmGizmo *gz);
 struct wmGizmoProperty *WM_gizmo_target_property_at_index(struct wmGizmo *gz, int index);
@@ -354,6 +357,10 @@ void WM_gizmo_group_type_unlink_delayed_ptr_ex(struct wmGizmoGroupType *gzgt,
 void WM_gizmo_group_type_unlink_delayed_ptr(struct wmGizmoGroupType *gzgt);
 void WM_gizmo_group_type_unlink_delayed(const char *idname);
 
+void WM_gizmo_group_unlink_delayed_ptr_from_space(struct wmGizmoGroupType *gzgt,
+                                                  struct wmGizmoMapType *gzmap_type,
+                                                  struct ScrArea *sa);
+
 /* Has the result of unlinking and linking (re-initializes gizmo's). */
 void WM_gizmo_group_type_reinit_ptr_ex(struct Main *bmain,
                                        struct wmGizmoGroupType *gzgt,
@@ -370,4 +377,6 @@ void WM_gizmo_group_remove_by_tool(struct bContext *C,
                                    const struct wmGizmoGroupType *gzgt,
                                    const struct bToolRef *tref);
 
+void WM_gizmo_group_tag_remove(struct wmGizmoGroup *gzgroup);
+
 #endif /* __WM_GIZMO_API_H__ */
index 7afd2908b8e2415fd437fa1935e02f46139bdffd..68ecdeea9361fa00bcbddaa53d05371b86a4b761 100644 (file)
@@ -417,6 +417,12 @@ typedef struct wmGizmoGroupType {
   /** Same as gizmo-maps, so registering/unregistering goes to the correct region. */
   struct wmGizmoMapType_Params gzmap_params;
 
+  /**
+   * Number of #wmGizmoGroup instances.
+   * Decremented when 'tag_remove' is set, or when removed.
+   */
+  int users;
+
 } wmGizmoGroupType;
 
 typedef struct wmGizmoGroup {
@@ -432,6 +438,8 @@ typedef struct wmGizmoGroup {
   /** Errors and warnings storage. */
   struct ReportList *reports;
 
+  bool tag_remove;
+
   void *customdata;
   /** For freeing customdata from above. */
   void (*customdata_free)(void *);
index f996d938dd7c5772daf144067a2afd727388cdac..40d99ce8ba67304f148b93888b2665e792ba99db 100644 (file)
@@ -70,7 +70,9 @@
 wmGizmoGroup *wm_gizmogroup_new_from_type(wmGizmoMap *gzmap, wmGizmoGroupType *gzgt)
 {
   wmGizmoGroup *gzgroup = MEM_callocN(sizeof(*gzgroup), "gizmo-group");
+
   gzgroup->type = gzgt;
+  gzgroup->type->users += 1;
 
   /* keep back-link */
   gzgroup->parent_gzmap = gzmap;
@@ -130,9 +132,23 @@ void wm_gizmogroup_free(bContext *C, wmGizmoGroup *gzgroup)
 
   BLI_remlink(&gzmap->groups, gzgroup);
 
+  if (gzgroup->tag_remove == false) {
+    gzgroup->type->users -= 1;
+  }
+
   MEM_freeN(gzgroup);
 }
 
+void WM_gizmo_group_tag_remove(wmGizmoGroup *gzgroup)
+{
+  if (gzgroup->tag_remove == false) {
+    gzgroup->tag_remove = true;
+    gzgroup->type->users -= 1;
+    BLI_assert(gzgroup->type->users >= 0);
+    WM_gizmoconfig_update_tag_group_remove(gzgroup->parent_gzmap);
+  }
+}
+
 /**
  * Add \a gizmo to \a gzgroup and make sure its name is unique within the group.
  */
@@ -1099,4 +1115,20 @@ void WM_gizmo_group_type_unlink_delayed(const char *idname)
   WM_gizmo_group_type_unlink_delayed_ptr(gzgt);
 }
 
+void WM_gizmo_group_unlink_delayed_ptr_from_space(wmGizmoGroupType *gzgt,
+                                                  wmGizmoMapType *gzmap_type,
+                                                  ScrArea *sa)
+{
+  for (ARegion *ar = sa->regionbase.first; ar; ar = ar->next) {
+    wmGizmoMap *gzmap = ar->gizmo_map;
+    if (gzmap && gzmap->type == gzmap_type) {
+      for (wmGizmoGroup *gzgroup = gzmap->groups.first; gzgroup; gzgroup = gzgroup->next) {
+        if (gzgroup->type == gzgt) {
+          WM_gizmo_group_tag_remove(gzgroup);
+        }
+      }
+    }
+  }
+}
+
 /** \} */
index 98c3ad8295ce7c279b5cfb7ccb489d9ff09140a4..f0771437518399ead9a4aab43d3c8a47f0c56bc5 100644 (file)
@@ -99,6 +99,9 @@ struct wmGizmoMap {
   /** Private, true when not yet used. */
   bool is_init;
 
+  /** When set, one of of the items in 'groups' has #wmGizmoGroup.tag_remove set. */
+  bool tag_remove_group;
+
   /**
    * \brief Gizmo map runtime context
    *
index edf376b12591e46e5bbb6a8d47d18aef30c07feb..be123bee5df7389321cd40d7b594e1aa9855ab54 100644 (file)
@@ -69,8 +69,13 @@ static ListBase gizmomaptypes = {NULL, NULL};
  */
 /* so operator removal can trigger update */
 typedef enum eWM_GizmoFlagGroupTypeGlobalFlag {
+  /** Initialize by #wmGroupType.type_update_flag. */
   WM_GIZMOMAPTYPE_GLOBAL_UPDATE_INIT = (1 << 0),
+  /** Remove by #wmGroupType.type_update_flag. */
   WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE = (1 << 1),
+
+  /** Remove by #wmGroup.tag_remove. */
+  WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE = (1 << 2),
 } eWM_GizmoFlagGroupTypeGlobalFlag;
 
 static eWM_GizmoFlagGroupTypeGlobalFlag wm_gzmap_type_update_flag = 0;
@@ -1257,6 +1262,13 @@ void WM_gizmoconfig_update_tag_group_type_remove(wmGizmoMapType *gzmap_type,
   wm_gzmap_type_update_flag |= WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE;
 }
 
+void WM_gizmoconfig_update_tag_group_remove(wmGizmoMap *gzmap)
+{
+  gzmap->tag_remove_group = true;
+
+  wm_gzmap_type_update_flag |= WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE;
+}
+
 /**
  * Run in case new types have been added (runs often, early exit where possible).
  * Follows #WM_keyconfig_update conventions.
@@ -1271,6 +1283,32 @@ void WM_gizmoconfig_update(struct Main *bmain)
     return;
   }
 
+  if (wm_gzmap_type_update_flag & WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE) {
+    for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
+        for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
+          ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+          for (ARegion *ar = regionbase->first; ar; ar = ar->next) {
+            wmGizmoMap *gzmap = ar->gizmo_map;
+            if (gzmap != NULL && gzmap->tag_remove_group) {
+              gzmap->tag_remove_group = false;
+
+              for (wmGizmoGroup *gzgroup = gzmap->groups.first, *gzgroup_next; gzgroup;
+                   gzgroup = gzgroup_next) {
+                gzgroup_next = gzgroup->next;
+                if (gzgroup->tag_remove) {
+                  wm_gizmogroup_free(NULL, gzgroup);
+                  ED_region_tag_redraw(ar);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    wm_gzmap_type_update_flag &= ~WM_GIZMOTYPE_GLOBAL_UPDATE_REMOVE;
+  }
+
   if (wm_gzmap_type_update_flag & WM_GIZMOMAPTYPE_GLOBAL_UPDATE_REMOVE) {
     for (wmGizmoMapType *gzmap_type = gizmomaptypes.first; gzmap_type;
          gzmap_type = gzmap_type->next) {