From 0847f2a368cb1c0235c04ee0400e546ac6b0e5e7 Mon Sep 17 00:00:00 2001
From: Guillem <guillembartrina@gmail.com>
Date: Mon, 24 Aug 2020 13:07:18 +0200
Subject: [PATCH] add comments and clean up the code

---
 pd/nw/pd_canvas.js   |  5 +--
 pd/nw/pdgui.js       | 10 +++---
 pd/src/g_canvas.c    | 76 +++++++++++++++++++++++++++++++++-----------
 pd/src/g_canvas.h    | 17 +++++-----
 pd/src/g_clone.c     | 24 ++++++++++----
 pd/src/g_editor.c    | 18 +++++++++--
 pd/src/g_readwrite.c |  2 +-
 pd/src/g_text.c      |  2 ++
 8 files changed, 109 insertions(+), 45 deletions(-)

diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index d5d133b89..f15b35832 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -1329,6 +1329,9 @@ function register_window_id(cid, attr_array) {
     // we check the title_queue to see if our title now contains an asterisk
     // (which is the visual cue for "dirty")
 
+    // Enable/disable the warning for multiple dirty instances
+    pdgui.gui_canvas_multipledirty(cid, attr_array.multipledirty);
+
     // Two possibilities for handling this better:
     // have a representation of canvas attys in pdgui.js (editmode, dirty, etc.)
     // or
@@ -1338,8 +1341,6 @@ function register_window_id(cid, attr_array) {
         nw.Window.get().title = kludge_title;
     }
     pdgui.free_title_queue(cid);
-
-    if(attr_array.muldirty) pdgui.gui_canvas_multipledirty(cid, 1);
 }
 
 function create_popup_menu(name) {
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 6c5bd3063..5aa5975c7 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -1607,7 +1607,7 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) {
 }
 
 // create a new canvas
-function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, muldirty, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
+function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, multipledirty, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
     // hack for buggy tcl popups... should go away for node-webkit
     //reset_ctrl_on_popup_window
     
@@ -1661,7 +1661,7 @@ function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir,
             name: name,
             dir: dir,
             dirty: dirty_flag,
-            muldirty: muldirty,
+            multipledirty: multipledirty,
             args: cargs,
             zoom: zoom,
             editmode: editmode,
@@ -2665,13 +2665,13 @@ function gui_gobj_dirty(cid, tag, state) {
         var border = e.querySelector(".border");
         border.classList.remove("dirty");
         border.classList.remove("subdirty");
-        if(state == 1) border.classList.add("dirty");
-        else if(state == 2) border.classList.add("subdirty");
+        if(state === 1) border.classList.add("dirty");
+        else if(state === 2) border.classList.add("subdirty");
     });
 }
 
 function gui_canvas_multipledirty(cid, state) {
-    var warning = patchwin[cid].window.document.getElementById("dirtywarning");
+    var warning = get_item(cid, "dirtywarning");
     if (state !== 0) warning.style.setProperty("display", "inline");
     else warning.style.setProperty("display", "none");
 }
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 933584cad..0c4476578 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -221,9 +221,12 @@ t_symbol *canvas_getcurrentdir(void)
     return (canvas_getdir(canvas_getcurrent()));
 }
 
+/* ** refactored function, check for errors */
 t_symbol *canvas_getdir(t_canvas *x)
 {
     x = canvas_getrootfor(x);
+    /* in the case the root is an ab instance, we borrow the
+        dir from the main root canvas (where the definition is stored) */
     if(x->gl_isab) x = x->gl_absource->ad_owner;
     t_canvasenvironment *e = canvas_getenv(x);
     return (e->ce_dir);
@@ -473,8 +476,9 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
     else x->gl_env = 0;
 
     x->gl_abdefs = 0;
-    if(canvas_newabsource) /* if absource is set means that this canvas is
-                                going to be an ab instance */
+    /* if canvas_newabsource is set means that
+        this canvas is going to be an ab instance */
+    if(canvas_newabsource)
     {
         x->gl_isab = 1;
         x->gl_absource = canvas_newabsource;
@@ -533,6 +537,8 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
     canvas_field_vec = NULL;
     canvas_field_gp = NULL;
 
+    /* in the case it is an abstraction (gl_env is not null)
+        get the number of dirty instances of this same abstraction */
     if(x->gl_env)
     {
         if(!x->gl_isab)
@@ -798,15 +804,20 @@ void canvas_reflecttitle(t_canvas *x)
 
 /* --------------------- */
 
+/* the following functions are used to broadcast messages to all instances of
+    a specific abstraction (either file-based or ab).
+    the state of these instances change accoring to the message sent. */
+
 void clone_iterate(t_pd *z, t_canvas_iterator it, void* data);
 int clone_match(t_pd *z, t_symbol *name, t_symbol *dir);
 
+/* packed data passing structure for canvas_dirty_broadcast */
 typedef struct _dirty_broadcast_data
 {
     t_symbol *name;
     t_symbol *dir;
     int mess;
-    int *res;
+    int *res;   /* return value */
 } t_dirty_broadcast_data;
 
 static void canvas_dirty_deliver_packed(t_canvas *x, t_dirty_broadcast_data *data)
@@ -831,10 +842,11 @@ static int canvas_dirty_broadcast(t_canvas *x, t_symbol *name, t_symbol *dir, in
                 && ((t_canvas *)g)->gl_name == name
                 && canvas_getdir((t_canvas *)g) == dir)
             {
-                res += (((t_canvas *)g)->gl_dirty > 0);
-                ((t_canvas *)g)->gl_dirties += mess;
-                if(((t_canvas *)g)->gl_havewindow)
-                    canvas_multipledirty((t_canvas *)g, (((t_canvas *)g)->gl_dirties > 1));
+                t_canvas *z = (t_canvas *)g;
+                res += (z->gl_dirty > 0);
+                z->gl_dirties += mess;
+                if(z->gl_havewindow)
+                    canvas_multipledirty(z, (z->gl_dirties > 1));
             }
             else
                 res += canvas_dirty_broadcast((t_canvas *)g, name, dir, mess);
@@ -902,10 +914,11 @@ static int canvas_dirty_broadcast_ab(t_canvas *x, t_ab_definition *abdef, int me
             if(canvas_isabstraction((t_canvas *)g) && ((t_canvas *)g)->gl_isab
                 && ((t_canvas *)g)->gl_absource == abdef)
             {
-                res += (((t_canvas *)g)->gl_dirty > 0);
-                ((t_canvas *)g)->gl_dirties += mess;
-                if(((t_canvas *)g)->gl_havewindow)
-                    canvas_multipledirty((t_canvas *)g, (((t_canvas *)g)->gl_dirties > 1));
+                t_canvas *z = (t_canvas *)g;
+                res += (z->gl_dirty > 0);
+                z->gl_dirties += mess;
+                if(z->gl_havewindow)
+                    canvas_multipledirty(z, (z->gl_dirties > 1));
             }
             else
                 res += canvas_dirty_broadcast_ab((t_canvas *)g, abdef, mess);
@@ -943,6 +956,10 @@ int canvas_dirty_broadcast_ab_all(t_ab_definition *abdef, int mess)
     return (res);
 }
 
+/* --------------------- */
+
+/* climbs up to the root canvas while enabling or disabling visual markings for dirtiness
+    of traversed canvases */
 void canvas_dirtyclimb(t_canvas *x, int n)
 {
     if (x->gl_owner)
@@ -972,8 +989,12 @@ void canvas_dirty(t_canvas *x, t_floatarg n)
         if (x2->gl_havewindow)
             canvas_reflecttitle(x2);
 
+        /* set dirtiness visual markings */
         canvas_dirtyclimb(x2, (unsigned)n);
 
+        /* in the case it is an abstraction, we tell all other
+            instances that there is eiher one more dirty instance or
+            one less dirty instance */
         if(canvas_isabstraction(x2)
             && (x2->gl_owner || x2->gl_isclone))
         {
@@ -1214,7 +1235,21 @@ void canvas_free(t_canvas *x)
 {
     //fprintf(stderr,"canvas_free %lx\n", (t_int)x);
 
-    if(x->gl_dirty)
+    /* crude hack. in the case it was a clone instance, it shouldn't have an owner.
+        For ab instances, we have set the owner inside clone_free because we need it
+        in order to deregister the dependencies.
+        here we set it to NULL again to prevent any error in the functions called bellow */
+    t_canvas *aux;
+    if(x->gl_isclone)
+    {
+        aux = x->gl_owner;
+        x->gl_owner = 0;
+    }
+
+    /* in the case it is a dirty abstraction, we tell all other
+        instances that there is one less dirty instance */
+    if(canvas_isabstraction(x) && x->gl_dirty
+        && (x->gl_owner || x->gl_isclone))
     {
         if(!x->gl_isab)
             canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), -1);
@@ -1264,7 +1299,8 @@ void canvas_free(t_canvas *x)
     if(x->gl_isab)
     {
         x->gl_absource->ad_numinstances--;
-        canvas_deregister_ab(x->gl_owner, x->gl_absource);
+        canvas_deregister_ab((x->gl_isclone ? aux : x->gl_owner),
+            x->gl_absource);
     }
 
     /* free stored ab definitions */
@@ -2144,9 +2180,10 @@ t_canvas *canvas_getrootfor_ab(t_canvas *x)
         return (canvas_getrootfor_ab(x->gl_owner));
 }
 
-/* check if the dependency graph has a cycle, assuming an new edge between parent and current nodes
-    if there is a cycle, a visual scheme of the cycle is stored in 'res' */
-static int ab_check_cycle(t_ab_definition *current, t_ab_definition *parent, int pathlen, char *path, char *res)
+/* check if the dependency graph has a cycle, assuming an new edge between parent and
+    current nodes if there is a cycle, a visual scheme of the cycle is stored in 'res' */
+static int ab_check_cycle(t_ab_definition *current, t_ab_definition *parent, int pathlen,
+    char *path, char *res)
 {
     if(current == parent)
     {
@@ -2156,7 +2193,7 @@ static int ab_check_cycle(t_ab_definition *current, t_ab_definition *parent, int
     }
     else
     {
-        /* if it is a local private abstraction, get rid of class member-like names (only used internally) */
+        /* if it is a local private abstraction, get rid of classmember-like names (only used internally) */
         char *hash = strrchr(current->ad_name->s_name, '#');
         if(!hash) hash = current->ad_name->s_name;
         else hash += 1;
@@ -2174,7 +2211,7 @@ static int ab_check_cycle(t_ab_definition *current, t_ab_definition *parent, int
 }
 
 /* try to register a new dependency into the dependency graph,
-    returns 0 and the scheme in 'res', if a dependency issue is found */
+    returns 0 and the scheme in 'res' if a dependency issue is found */
 static int canvas_register_ab(t_canvas *x, t_ab_definition *a, char *res)
 {
     /* climb to closest ab */
@@ -2268,7 +2305,7 @@ static t_ab_definition *canvas_find_ab(t_canvas *x, t_symbol *name)
     return 0;
 }
 
-/* adds a new ab definition. returns the definition if it has been added, 0 otherwise */
+/* tries to add a new ab definition. returns the definition if it has been added, 0 otherwise */
 static t_ab_definition *canvas_add_ab(t_canvas *x, t_symbol *name, t_binbuf *source)
 {
     if(!canvas_find_ab(x, name))
@@ -2410,6 +2447,7 @@ static void canvas_abpush(t_canvas *x, t_symbol *s, int argc, t_atom *argv)
     pd_free(&x->gl_pd);
 }
 
+/* extends the name for a local ab, using a classmember-like format */
 static t_symbol *ab_extend_name(t_canvas *x, t_symbol *s)
 {
     char res[MAXPDSTRING];
diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index aab227d02..26c492e5f 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -165,19 +165,19 @@ typedef struct _tick    /* where to put ticks on x or y axes */
 } t_tick;
 
 /* the t_ab_definition structure holds an ab definiton and all the attributes we need
-    to handle them */
+    to handle it */
 typedef struct _ab_definition
 {
     t_symbol *ad_name;      /* id for the ab definition */
-    t_binbuf *ad_source;    /* binbuf where source is stored */
-    int ad_numinstances;    /* the num of instances of this abstraction */
-    struct _ab_definition *ad_next; /* next ab definition */
-    t_canvas *ad_owner;
+    t_binbuf *ad_source;    /* binbuf where the source is stored */
+    int ad_numinstances;    /* number of instances */
+    struct _ab_definition *ad_next;     /* next ab definition */
+    t_canvas *ad_owner;     /* canvas that stores this definition */
 
     /* dependency graph stuff */
     int ad_numdep;      /* number of other ab definitions that it depends on */
     struct _ab_definition **ad_dep;     /* the actual ab defintitions */
-    int *ad_deprefs;
+    int *ad_deprefs;    /*  number of instances that define the dependency */
     int ad_visflag;     /* visited flag for topological sort algorithm */
 } t_ab_definition;
 
@@ -251,8 +251,8 @@ struct _glist
     t_word *gl_vec;            /* for "canvas" data type */
     t_gpointer gl_gp;            /* parent for "canvas" data type */
 
-    unsigned int gl_subdirties;     /* number of descending dirty abstractions */
-    int gl_dirties;    /* number of diry instances of this type */
+    int gl_subdirties;     /* number of descending dirty abstractions */
+    int gl_dirties;        /* number of diry instances, for multiple dirty warning */
 
     unsigned int gl_isab:1;         /* is an ab instance */
     t_ab_definition *gl_absource;   /* ab definition pointer,
@@ -601,7 +601,6 @@ EXTERN t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
     int *x1p, int *y1p, int *x2p, int *y2p);
 EXTERN int canvas_setdeleting(t_canvas *x, int flag);
 EXTERN int canvas_hasarray(t_canvas *x);
-
 EXTERN void canvas_multipledirty(t_canvas *x, int on);
 
 #define LB_LOAD 0       /* "loadbang" actions - 0 for original meaning */
diff --git a/pd/src/g_clone.c b/pd/src/g_clone.c
index 7dc43a9a5..d5ba76414 100644
--- a/pd/src/g_clone.c
+++ b/pd/src/g_clone.c
@@ -63,9 +63,11 @@ typedef struct _clone
     int x_phase;
     int x_startvoice;   /* number of first voice, 0 by default */
     int x_suppressvoice; /* suppress voice number as $1 arg */
-    t_canvas *x_owner;
+    t_canvas *x_owner;  /* clone owner */
 } t_clone;
 
+/* the given 'it' function is executed over each of the underlying canvases
+    (they are passed as first parameter). 'data' is passed as second argument */
 void clone_iterate(t_pd *z, t_canvas_iterator it, void* data)
 {
     t_clone *x = (t_clone *)z;
@@ -199,8 +201,13 @@ static void clone_free(t_clone *x)
         for (i = 0; i < x->x_n; i++)
         {
             canvas_closebang(x->x_vec[i].c_gl);
-            if(x->x_vec[i].c_gl->gl_isab) //only for abs?
+            if(x->x_vec[i].c_gl->gl_isab)
+            {
+                /* crude hack. since clones don't have owner,
+                    we set it manually to allow the clone to
+                    deregister the dependencies */
                 x->x_vec[i].c_gl->gl_owner = x->x_owner;
+            }
             pd_free(&x->x_vec[i].c_gl->gl_pd);
             t_freebytes(x->x_outvec[i],
                 x->x_nout * sizeof(*x->x_outvec[i]));
@@ -254,6 +261,7 @@ void clone_setn(t_clone *x, t_floatarg f)
     {
         t_canvas *c;
         t_out *outvec;
+        /* in the case they are [ab]s, the instance number is one atom beyond */
         SETFLOAT((x->x_vec[0].c_gl->gl_isab ? x->x_argv+1 : x->x_argv), x->x_startvoice + i);
         if (!(c = clone_makeone(x->x_s, x->x_argc - x->x_suppressvoice,
             x->x_argv + x->x_suppressvoice)))
@@ -380,7 +388,7 @@ static void clone_dsp(t_clone *x, t_signal **sp)
     }
 }
 
-static int clone_newab = 0;
+static int clone_newabclone = 0;
 static void *clone_new(t_symbol *s, int argc, t_atom *argv)
 {
     t_clone *x = (t_clone *)pd_new(clone_class);
@@ -420,7 +428,10 @@ static void *clone_new(t_symbol *s, int argc, t_atom *argv)
     else goto usage;
         /* store a copy of the argmuents with an extra space (argc+1) for
         supplying an instance number, which we'll bash as we go. */
-    if(clone_newab)
+    if(clone_newabclone)
+    /* we are creating a clone from an [ab] definition, we use the same creation
+        method as for normal clones but the name we pass to objectmaker is
+        'ab <name>' instead of just '<name>' */
     {
         x->x_argc = argc;
         x->x_argv = getbytes(x->x_argc * sizeof(*x->x_argv));
@@ -429,7 +440,7 @@ static void *clone_new(t_symbol *s, int argc, t_atom *argv)
         SETFLOAT(x->x_argv+1, x->x_startvoice);
         x->x_s = gensym("ab");
         x->x_owner = canvas_getcurrent();
-        clone_newab = 0;
+        clone_newabclone = 0;
     }
     else
     {
@@ -485,9 +496,10 @@ fail:
     return (0);
 }
 
+/* creator for [ab]-based clones */
 static void *abclone_new(t_symbol *s, int argc, t_atom *argv)
 {
-    clone_newab = 1;
+    clone_newabclone = 1;
     return clone_new(s, argc, argv);
 }
 
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 519f57735..29feb81d1 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -1294,6 +1294,7 @@ int clone_match(t_pd *z, t_symbol *name, t_symbol *dir);
 int clone_isab(t_pd *z);
 int clone_matchab(t_pd *z, t_ab_definition *source);
 
+/* packed data passing structure for glist_doreload */
 typedef struct _reload_data
 {
     t_symbol *n;
@@ -1327,7 +1328,7 @@ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
                 ((t_canvas *)g)->gl_name == name &&
                     canvas_getdir((t_canvas *)g) == dir);
 
-        /* set dirty to 0 to remove the dirty markings*/
+        /* remove dirtiness visual markings */
         if(remakeit && ((t_canvas *)g)->gl_dirty)
             canvas_dirtyclimb((t_canvas *)g, 0);
 
@@ -1398,6 +1399,9 @@ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
            )
                 glist_doreload((t_canvas *)g, name, dir, except);
 
+        /* also reload the instances within ab-based clone objects
+            *COMMENT: missing recursive case for abstraction-based clone
+                objects that don't match with the one we are reloading?  */
         if(pd_class(&g->g_pd) == clone_class && clone_isab(&g->g_pd))
         {
             t_reload_data d;
@@ -1432,6 +1436,7 @@ void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except)
     canvas_resume_dsp(dspwas);
 }
 
+/* packed data passing structure for glist_doreload_ab */
 typedef struct _reload_ab_data
 {
     t_ab_definition *a;
@@ -1452,7 +1457,7 @@ static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e)
         remakeit = (g != e && pd_class(&g->g_pd) == canvas_class && canvas_isabstraction((t_canvas *)g)
             && ((t_canvas *)g)->gl_isab && ((t_canvas *)g)->gl_absource == a);
 
-        /* set dirty to 0 to remove the dirty markings*/
+        /* remove dirtiness visual markings */
         if(remakeit && ((t_canvas *)g)->gl_dirty)
             canvas_dirtyclimb((t_canvas *)g, 0);
 
@@ -1468,6 +1473,7 @@ static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e)
             }
             glist_select(x, g);
         }
+        /* since this one won't be reloaded, we need to trigger initbang manually */
         else if(g == e)
             canvas_initbang((t_canvas *)g);
         g = g->g_next;
@@ -1485,6 +1491,7 @@ static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e)
                     || (((t_canvas *)g)->gl_isab && (((t_canvas *)g)->gl_absource != a))))
             glist_doreload_ab((t_canvas *)g, a, e);
 
+        /* also reload the instances within ab-based clone objects */
         if(pd_class(&g->g_pd) == clone_class
             && clone_isab(&g->g_pd) && !clone_matchab(&g->g_pd, a))
         {
@@ -1492,7 +1499,6 @@ static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e)
             d.a = a; d.e = e;
             clone_iterate(&g->g_pd, glist_doreload_ab_packed, &d);
         }
-
         g = g->g_next;
     }
 }
@@ -1513,6 +1519,9 @@ void canvas_reload_ab(t_canvas *x)
     glist_doreload_ab(c, x->gl_absource, &x->gl_gobj);
     glist_amreloadingabstractions = 0;
     canvas_resume_dsp(dspwas);
+    /* we set the dirty flag of the root canvas (where the ab definitions
+        are stored) because its file contents (in this case, the definition
+        for an specific ab) has been edited */
     canvas_dirty(c, 1);
 }
 
@@ -5962,12 +5971,15 @@ static void gobj_emphasize(t_glist *g, t_gobj *x)
     gui_vmess("gui_gobj_emphasize", "xs", g, rtext_gettag(y));
 }
 
+    /* tell the gui to mark a gobj as dirty (change border color) */
 void gobj_dirty(t_gobj *x, t_glist *g, int state)
 {
     t_rtext *y = glist_findrtext(g, (t_text *)x);
     gui_vmess("gui_gobj_dirty", "xsi", g, rtext_gettag(y), state);
 }
 
+    /* tell the gui to display a warning about the existence of
+        multiple dirty instances of the same abstraction */
 void canvas_multipledirty(t_canvas *x, int on)
 {
     gui_vmess("gui_canvas_multipledirty", "xi", x, (on > 0));
diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c
index 9c591dbf8..007dcc0ee 100644
--- a/pd/src/g_readwrite.c
+++ b/pd/src/g_readwrite.c
@@ -982,7 +982,7 @@ static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir,
 
 void canvas_reload_ab(t_canvas *x);
 
-/* updates the shared ab definition and reloads all the instances */
+/* updates the shared ab definition and reloads all instances */
 static void canvas_save_ab(t_canvas *x, t_floatarg fdestroy)
 {
     if(!x->gl_absource) bug("canvas_save_ab");
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 501dd0b0c..6c875164c 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -2122,6 +2122,8 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
                     rtext_width(y), rtext_height(y), 1);
                 rtext_draw(y);
 
+                /* check whether we have to tell the gui to mark
+                    (border color) the gobj as dirty or not */
                 if(pd_class(&x->te_pd) == canvas_class)
                 {
                     if (((t_canvas *)x)->gl_dirty)
-- 
GitLab