changeset 2337:ef078d219bfc

GTK4: Add a shadow surface to GtkDrawingArea widgets, which all the drawing functions draw on. When drawing to a widget add it to the dirty list if it doesn't already exist. dw_flush() will trigger a redraw on all dirty widgets, and then clear the dirty list.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Tue, 02 Mar 2021 14:55:01 +0000
parents dd1b2cc95c91
children b9b10bb1f689
files gtk4/dw.c
diffstat 1 files changed, 75 insertions(+), 139 deletions(-) [+]
line wrap: on
line diff
--- a/gtk4/dw.c	Tue Mar 02 10:30:10 2021 +0000
+++ b/gtk4/dw.c	Tue Mar 02 14:55:01 2021 +0000
@@ -386,12 +386,14 @@
 # define min(a,b)        (((a) < (b)) ? (a) : (b))
 #endif
 
-pthread_key_t _dw_fg_color_key;
-pthread_key_t _dw_bg_color_key;
-pthread_key_t _dw_event_key;
+static pthread_key_t _dw_fg_color_key;
+static pthread_key_t _dw_bg_color_key;
+static pthread_key_t _dw_event_key;
 
 static pthread_t _dw_thread = (pthread_t)-1;
 
+static GList *_dw_dirty_list = NULL;
+
 #define _DW_TREE_TYPE_CONTAINER  1
 #define _DW_TREE_TYPE_TREE       2
 #define _DW_TREE_TYPE_LISTBOX    3
@@ -828,6 +830,37 @@
    return retval;
 }
 
+cairo_t *_dw_cairo_update(GtkWidget *widget, int width, int height)
+{
+   cairo_t *wincr = g_object_get_data(G_OBJECT(widget), "_dw_cr"); 
+   cairo_surface_t *surface = g_object_get_data(G_OBJECT(widget), "_dw_cr_surface"); 
+
+   if(width == -1 && height == -1 && g_list_find(_dw_dirty_list, widget) == NULL)
+      _dw_dirty_list = g_list_append(_dw_dirty_list, widget);
+
+   if(width == -1)
+      width = gtk_widget_get_width(widget);
+   if(height == -1)
+      height = gtk_widget_get_height(widget);
+   
+   if(!wincr || GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "_dw_width")) != width ||
+      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "_dw_height")) != height)
+   {
+      if(wincr)
+         cairo_destroy(wincr);
+      if(surface)
+         cairo_surface_destroy(surface);
+      surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+      wincr = cairo_create(surface);
+      /* Save the cairo context for use in the drawing functions */
+      g_object_set_data(G_OBJECT(widget), "_dw_cr", (gpointer)wincr);
+      g_object_set_data(G_OBJECT(widget), "_dw_cr_surface", (gpointer)surface);
+      g_object_set_data(G_OBJECT(widget), "_dw_width", GINT_TO_POINTER(width));
+      g_object_set_data(G_OBJECT(widget), "_dw_height", GINT_TO_POINTER(height));
+   }
+   return wincr;
+}
+
 static gint _dw_expose_event(GtkWidget *widget, cairo_t *cr, int width, int height, gpointer data)
 {
    int retval = FALSE;
@@ -837,13 +870,16 @@
       DWExpose exp;
       int (*exposefunc)(HWND, DWExpose *, void *) = g_object_get_data(G_OBJECT(widget), "_dw_expose_func");
 
+      _dw_cairo_update(widget, width, height);
+
       exp.x = exp.y = 0;
       exp.width = width;
       exp.height = height;
-      /* Save the cairo context for use in the drawing functions */
-      g_object_set_data(G_OBJECT(widget), "_dw_cr", (gpointer)cr);
       retval = exposefunc((HWND)widget, &exp, data);
-      g_object_set_data(G_OBJECT(widget), "_dw_cr", NULL);
+      /* Copy the cached image to the outbut surface */
+      cairo_set_source_surface(cr, g_object_get_data(G_OBJECT(widget), "_dw_cr_surface"), 0, 0);
+      cairo_rectangle(cr, 0, 0, width, height);
+      cairo_fill(cr);
    }
    return retval;
 }
@@ -2193,7 +2229,7 @@
       {
          int len, x;
 
-         font = pango_font_description_to_string( pfont );
+         font = pango_font_description_to_string(pfont);
          retfont = strdup(font);
          len = strlen(font);
          /* Convert to Dynamic Windows format if we can... */
@@ -6446,26 +6482,12 @@
 DW_FUNCTION_RESTORE_PARAM4(handle, HWND, pixmap, HPIXMAP, x, int, y, int)
 {
    cairo_t *cr = NULL;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(handle)
    {
-      if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+      if((cr = _dw_cairo_update(handle, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(handle);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(pixmap)
       cr = cairo_create(pixmap->image);
@@ -6480,9 +6502,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
    }
    DW_FUNCTION_RETURN_NOTHING;
@@ -6503,26 +6523,12 @@
 DW_FUNCTION_RESTORE_PARAM6(handle, HWND, pixmap, HPIXMAP, x1, int, y1, int, x2, int, y2, int)
 {
    cairo_t *cr = NULL;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(handle)
    {
-      if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+      if((cr = _dw_cairo_update(handle, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(handle);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(pixmap)
       cr = cairo_create(pixmap->image);
@@ -6538,9 +6544,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
    }
    DW_FUNCTION_RETURN_NOTHING;
@@ -6562,26 +6566,12 @@
 {
    cairo_t *cr = NULL;
    int z;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(handle)
    {
-      if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+      if((cr = _dw_cairo_update(handle, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(handle);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(pixmap)
       cr = cairo_create(pixmap->image);
@@ -6605,9 +6595,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
    }
    DW_FUNCTION_RETURN_NOTHING;
@@ -6629,26 +6617,12 @@
 DW_FUNCTION_RESTORE_PARAM7(handle, HWND, pixmap, HPIXMAP, flags, int, x, int, y, int, width, int, height, int)
 {
    cairo_t *cr = NULL;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(handle)
    {
-      if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+      if((cr = _dw_cairo_update(handle, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(handle);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(pixmap)
       cr = cairo_create(pixmap->image);
@@ -6671,9 +6645,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
    }
    DW_FUNCTION_RETURN_NOTHING;
@@ -6698,26 +6670,12 @@
 DW_FUNCTION_RESTORE_PARAM9(handle, HWND, pixmap, HPIXMAP, flags, int, xorigin, int, yorigin, int, x1, int, y1, int, x2, int, y2, int)
 {
    cairo_t *cr = NULL;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(handle)
    {
-      if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+      if((cr = _dw_cairo_update(handle, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(handle);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(pixmap)
       cr = cairo_create(pixmap->image);
@@ -6752,9 +6710,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
    }
    DW_FUNCTION_RETURN_NOTHING;
@@ -6778,26 +6734,12 @@
       cairo_t *cr = NULL;
       PangoFontDescription *font;
       char *tmpname, *fontname = "monospace 10";
-      GdkDrawContext *dc = NULL;
       int cached = FALSE;
 
       if(handle)
       {
-         if((cr = g_object_get_data(G_OBJECT(handle), "_dw_cr")))
+         if((cr = _dw_cairo_update(handle, -1, -1)))
             cached = TRUE;
-         else
-         {
-            GtkNative *native = gtk_widget_get_native(handle);
-            GdkSurface *surface = gtk_native_get_surface(native);
-
-            if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-            {
-               cairo_region_t *region = cairo_region_create();
-               gdk_draw_context_begin_frame(dc, region);
-               cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-               cairo_region_destroy(region);
-            }
-         }
          if((tmpname = (char *)g_object_get_data(G_OBJECT(handle), "_dw_fontname")))
             fontname = tmpname;
       }
@@ -6856,9 +6798,7 @@
          /* If we are using a drawing context...
           * we don't own the cairo context so don't destroy it.
           */
-         if(dc)
-            gdk_draw_context_end_frame(dc);
-         else if(!cached)
+         if(!cached)
             cairo_destroy(cr);
       }
    }
@@ -7111,11 +7051,22 @@
    DW_FUNCTION_RETURN_THIS(pixmap);
 }
 
+static void _dw_flush_dirty(gpointer widget, gpointer data)
+{
+   gtk_widget_queue_draw(GTK_WIDGET(widget));
+}
+
 /* Call this after drawing to the screen to make sure
  * anything you have drawn is visible.
  */
-void API dw_flush(void)
-{
+DW_FUNCTION_DEFINITION(dw_flush, void)
+DW_FUNCTION_ADD_PARAM
+DW_FUNCTION_NO_RETURN(dw_flush)
+{
+   g_list_foreach(_dw_dirty_list, _dw_flush_dirty, NULL);
+   g_list_free(_dw_dirty_list);
+   _dw_dirty_list = NULL;
+   DW_FUNCTION_RETURN_NOTHING;
 }
 
 /*
@@ -7208,26 +7159,12 @@
 {
    cairo_t *cr = NULL;
    int retval = DW_ERROR_GENERAL;
-   GdkDrawContext *dc = NULL;
    int cached = FALSE;
 
    if(dest)
    {
-      if((cr = g_object_get_data(G_OBJECT(dest), "_dw_cr")))
+      if((cr = _dw_cairo_update(dest, -1, -1)))
          cached = TRUE;
-      else
-      {
-         GtkNative *native = gtk_widget_get_native(dest);
-         GdkSurface *surface = gtk_native_get_surface(native);
-
-         if((dc = GDK_DRAW_CONTEXT(gdk_surface_create_cairo_context(surface))))
-         {
-            cairo_region_t *region = cairo_region_create();
-            gdk_draw_context_begin_frame(dc, region);
-            cr = gdk_cairo_context_cairo_create(GDK_CAIRO_CONTEXT(dc));
-            cairo_region_destroy(region);
-         }
-      }
    }
    else if(destp)
       cr = cairo_create(destp->image);
@@ -7244,10 +7181,11 @@
       }
 
       if(src)
-         ;
-#ifdef GTK3 /* TODO: See how to do this in GTK4 */   
-         gdk_cairo_set_source_window (cr, gtk_widget_get_window(src), (xdest + xsrc) / xscale, (ydest + ysrc) / yscale);
-#endif         
+      {
+         cairo_surface_t *surface = g_object_get_data(G_OBJECT(src), "_dw_cr_surface");
+         if(surface)
+            cairo_set_source_surface (cr, surface, (xdest + xsrc) / xscale, (ydest + ysrc) / yscale);
+      }        
       else if(srcp)
          cairo_set_source_surface (cr, srcp->image, (xdest + xsrc) / xscale, (ydest + ysrc) / yscale);
 
@@ -7256,9 +7194,7 @@
       /* If we are using a drawing context...
        * we don't own the cairo context so don't destroy it.
        */
-      if(dc)
-         gdk_draw_context_end_frame(dc);
-      else if(!cached)
+      if(!cached)
          cairo_destroy(cr);
       retval = DW_ERROR_NONE;
    }