pidgin/plugins/gestures/stroke-draw.c

changeset 40497
2f45a03838e9
parent 40198
0eac0eaf13c3
child 40669
48dfed6f4f1f
--- a/pidgin/plugins/gestures/stroke-draw.c	Thu Jul 23 20:13:47 2020 -0500
+++ b/pidgin/plugins/gestures/stroke-draw.c	Fri Jul 24 04:43:46 2020 -0500
@@ -11,19 +11,14 @@
 #include <stdio.h>
 #include <glib.h>
 #include <gtk/gtk.h>
-#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
 
 #include "gstroke.h"
 #include "gstroke-internal.h"
 
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-static void gstroke_invisible_window_init (GtkWidget *widget);
+static gboolean gstroke_draw_cb(GtkWidget *widget, cairo_t *cr,
+                                gpointer user_data);
 /*FIXME: Maybe these should be put in a structure, and not static...*/
-static Display * gstroke_disp = NULL;
-static Window gstroke_window;
-static GC gstroke_gc;
 static int mouse_button = 2;
 static gboolean draw_strokes = FALSE;
 
@@ -65,24 +60,7 @@
 	gdk_window_get_device_position(gtk_widget_get_window(widget),
 		dev, &x, &y, NULL);
 
-	if (last_mouse_position.invalid)
-		last_mouse_position.invalid = FALSE;
-	else if (gstroke_draw_strokes()) {
-#if 1
-		XDrawLine(gstroke_disp, gstroke_window, gstroke_gc,
-			last_mouse_position.last_point.x,
-			last_mouse_position.last_point.y, x, y);
-		/* XFlush (gstroke_disp); */
-#else
-		/* FIXME: this does not work. It will only work if we create
-		 * a corresponding GDK window for stroke_window and draw on
-		 * that... */
-		gdk_draw_line(gtk_widget_get_window(widget),
-			widget->style->fg_gc[GTK_STATE_NORMAL],
-			last_mouse_position.last_point.x,
-			last_mouse_position.last_point.y, x, y);
-#endif
-	}
+	last_mouse_position.invalid = FALSE;
 
 	if (last_mouse_position.last_point.x != x ||
 		last_mouse_position.last_point.y != y)
@@ -92,6 +70,10 @@
 		metrics = g_object_get_data(G_OBJECT(widget), GSTROKE_METRICS);
 		_gstroke_record (x, y, metrics);
 	}
+
+	if (gstroke_draw_strokes()) {
+		gtk_widget_queue_draw(widget);
+	}
 }
 
 static gint
@@ -107,7 +89,8 @@
 	return TRUE;
 }
 
-static void gstroke_cancel(GdkEvent *event)
+static void
+gstroke_cancel(GtkWidget *widget, GdkEvent *event)
 {
 	last_mouse_position.invalid = TRUE;
 
@@ -120,16 +103,13 @@
 		gdk_seat_ungrab(gdk_event_get_seat(event));
 	}
 
-	if (gstroke_draw_strokes() && gstroke_disp != NULL) {
-	    /* get rid of the invisible stroke window */
-	    XUnmapWindow (gstroke_disp, gstroke_window);
-	    XFlush (gstroke_disp);
+	if (gstroke_draw_strokes()) {
+		gtk_widget_queue_draw(widget);
 	}
-
 }
 
 static gint
-process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED)
+process_event(GtkWidget *widget, GdkEvent *event, gpointer data)
 {
   static GtkWidget *original_widget = NULL;
   static GdkCursor *cursor = NULL;
@@ -141,7 +121,7 @@
 			 * clicked after the middle button is clicked (but possibly
 			 * not released)
 			 */
-			gstroke_cancel(event);
+				gstroke_cancel(widget, event);
 			original_widget = NULL;
 			break;
 		}
@@ -149,8 +129,6 @@
       original_widget = widget; /* remeber the widget where
                                    the stroke started */
 
-      gstroke_invisible_window_init (widget);
-
       record_stroke_segment (widget);
 
 	  if (cursor == NULL) {
@@ -171,7 +149,7 @@
 
 		/* Nice bug when you hold down one button and press another. */
 		/* We'll just cancel the gesture instead. */
-		gstroke_cancel(event);
+				gstroke_cancel(widget, event);
 		original_widget = NULL;
 		break;
 	  }
@@ -182,21 +160,20 @@
 	  gdk_seat_ungrab(gdk_event_get_seat(event));
       timer_id = 0;
 
-      {
+			{
+				GtkWidget *history = data;
 	char result[GSTROKE_MAX_SEQUENCE];
 	struct gstroke_metrics *metrics;
 
 	metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget),
 														  GSTROKE_METRICS);
 		if (gstroke_draw_strokes()) {
-			/* get rid of the invisible stroke window */
-			XUnmapWindow (gstroke_disp, gstroke_window);
-			XFlush (gstroke_disp);
+					gtk_widget_queue_draw(widget);
 		}
 
-	_gstroke_canonical (result, metrics);
-	gstroke_execute (widget, result);
-      }
+				_gstroke_canonical(result, metrics);
+				gstroke_execute(history, result);
+			}
       return FALSE;
 
     default:
@@ -233,58 +210,85 @@
 void
 gstroke_enable (GtkWidget *widget)
 {
-  struct gstroke_metrics*
-    metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
-														  GSTROKE_METRICS);
-  if (metrics == NULL)
-    {
-      metrics = (struct gstroke_metrics *)g_malloc (sizeof
-                                                    (struct gstroke_metrics));
-      metrics->pointList = NULL;
-      metrics->min_x = 10000;
-      metrics->min_y = 10000;
-      metrics->max_x = 0;
-      metrics->max_y = 0;
-      metrics->point_count = 0;
+	GtkWidget *event = gtk_widget_get_parent(widget);
+	struct gstroke_metrics *metrics = NULL;
+
+	if (GTK_IS_EVENT_BOX(event)) {
+		metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(event),
+		                                                      GSTROKE_METRICS);
+	}
+
+	if (metrics == NULL) {
+		GtkWidget *parent;
+
+		metrics = g_new0(struct gstroke_metrics, 1);
+		metrics->pointList = NULL;
+		metrics->min_x = 10000;
+		metrics->min_y = 10000;
+		metrics->max_x = 0;
+		metrics->max_y = 0;
+		metrics->point_count = 0;
 
-      g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics);
+		event = gtk_event_box_new();
+		gtk_event_box_set_above_child(GTK_EVENT_BOX(event), TRUE);
+		gtk_widget_set_events(event, GDK_BUTTON_PRESS_MASK |
+		                                     GDK_BUTTON_RELEASE_MASK |
+		                                     GDK_BUTTON2_MOTION_MASK);
+		gtk_widget_set_app_paintable(event, TRUE);
+		gtk_widget_show(event);
 
-      g_signal_connect(G_OBJECT(widget), "event",
-					   G_CALLBACK(process_event), NULL);
-    }
-  else
-    _gstroke_init (metrics);
+		parent = gtk_widget_get_parent(widget);
+		g_object_ref(widget);
+		gtk_container_remove(GTK_CONTAINER(parent), widget);
+		gtk_container_add(GTK_CONTAINER(event), widget);
+		g_object_unref(widget);
+		gtk_container_add(GTK_CONTAINER(parent), event);
+
+		g_object_set_data(G_OBJECT(event), GSTROKE_METRICS, metrics);
 
-  last_mouse_position.invalid = TRUE;
+		g_signal_connect(G_OBJECT(event), "event", G_CALLBACK(process_event),
+		                 widget);
+		g_signal_connect_after(G_OBJECT(event), "draw",
+		                       G_CALLBACK(gstroke_draw_cb), NULL);
+	} else {
+		_gstroke_init(metrics);
+	}
+
+	last_mouse_position.invalid = TRUE;
 }
 
 void
 gstroke_disable(GtkWidget *widget)
 {
-  g_signal_handlers_disconnect_by_func(G_OBJECT(widget), G_CALLBACK(process_event), NULL);
+	GtkWidget *event = gtk_widget_get_parent(widget);
+
+	g_return_if_fail(GTK_IS_EVENT_BOX(event));
+
+	g_signal_handlers_disconnect_by_func(G_OBJECT(event),
+	                                     G_CALLBACK(process_event), widget);
+	g_signal_handlers_disconnect_by_func(G_OBJECT(event),
+	                                     G_CALLBACK(gstroke_draw_cb), NULL);
 }
 
-guint
-gstroke_signal_connect (GtkWidget *widget,
-                        const gchar *name,
-                        void (*func)(GtkWidget *widget, void *data),
-                        gpointer data)
+void
+gstroke_signal_connect(GtkWidget *widget, const gchar *name,
+                       void (*func)(GtkWidget *widget, void *data),
+                       gpointer data)
 {
-  struct gstroke_func_and_data *func_and_data;
-  GHashTable *hash_table =
-    (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
+	struct gstroke_func_and_data *func_and_data;
+	GHashTable *hash_table =
+	        (GHashTable *)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
 
-  if (!hash_table)
-    {
-      hash_table = g_hash_table_new (g_str_hash, g_str_equal);
-      g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS,
-						(gpointer)hash_table);
-    }
-  func_and_data = g_new (struct gstroke_func_and_data, 1);
-  func_and_data->func = func;
-  func_and_data->data = data;
-  g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data);
-  return TRUE;
+	if (!hash_table) {
+		hash_table =
+		        g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+		g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS, hash_table);
+	}
+
+	func_and_data = g_new0(struct gstroke_func_and_data, 1);
+	func_and_data->func = func;
+	func_and_data->data = data;
+	g_hash_table_insert(hash_table, g_strdup(name), func_and_data);
 }
 
 static void
@@ -310,100 +314,62 @@
 void
 gstroke_cleanup (GtkWidget *widget)
 {
-  struct gstroke_metrics *metrics;
-  GHashTable *hash_table =
-    (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
-  if (hash_table)
-    /*  FIXME: does this delete the elements too?  */
-    g_hash_table_destroy (hash_table);
+	struct gstroke_metrics *metrics;
+	GHashTable *hash_table = (GHashTable *)g_object_steal_data(G_OBJECT(widget),
+	                                                           GSTROKE_SIGNALS);
+	if (hash_table) {
+		g_hash_table_destroy(hash_table);
+	}
 
-  g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS);
-
-  metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget),
-													   GSTROKE_METRICS);
-  g_free(metrics);
-  g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS);
+	metrics = (struct gstroke_metrics *)g_object_steal_data(G_OBJECT(widget),
+	                                                        GSTROKE_METRICS);
+	g_free(metrics);
 }
 
-
-/* This function should be written using GTK+ primitives*/
-static void
-gstroke_invisible_window_init (GtkWidget *widget)
+static gboolean
+gstroke_draw_cb(GtkWidget *widget, cairo_t *cr,
+                G_GNUC_UNUSED gpointer user_data)
 {
-  XSetWindowAttributes w_attr;
-  XWindowAttributes orig_w_attr;
-  unsigned long mask, col_border, col_background;
-  unsigned int border_width;
-  XSizeHints hints;
-  Display *disp = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget));
-  Window wind = gdk_x11_window_get_xid(gtk_widget_get_window(widget));
-  int screen = DefaultScreen (disp);
-
-	if (!gstroke_draw_strokes())
-		return;
-
-  gstroke_disp = disp;
+	struct gstroke_metrics *metrics =
+	        (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
+	                                                    GSTROKE_METRICS);
+	GSList *iter = NULL;
+	p_point point;
 
-  /* X server should save what's underneath */
-  XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr);
-  hints.x = orig_w_attr.x;
-  hints.y = orig_w_attr.y;
-  hints.width = orig_w_attr.width;
-  hints.height = orig_w_attr.height;
-  mask = CWSaveUnder;
-  w_attr.save_under = True;
+	if (last_mouse_position.invalid) {
+		return FALSE;
+	}
 
-  /* inhibit all the decorations */
-  mask |= CWOverrideRedirect;
-  w_attr.override_redirect = True;
+	if (!metrics) {
+		return FALSE;
+	}
 
-  /* Don't set a background, transparent window */
-  mask |= CWBackPixmap;
-  w_attr.background_pixmap = None;
-
-  /* Default input window look */
-  col_background = WhitePixel (gstroke_disp, screen);
+	iter = metrics->pointList;
+	if (!iter) {
+		return FALSE;
+	}
 
-  /* no border for the window */
-#if 0
-  border_width = 5;
-#endif
-  border_width = 0;
-
-  col_border = BlackPixel (gstroke_disp, screen);
+	cairo_save(cr);
 
-  gstroke_window = XCreateSimpleWindow (gstroke_disp, wind,
-                                        0, 0,
-                                        hints.width - 2 * border_width,
-                                        hints.height - 2 * border_width,
-                                        border_width,
-                                        col_border, col_background);
+	cairo_set_line_width(cr, 2.0);
+	cairo_set_dash(cr, NULL, 0, 0.0);
+	cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+	cairo_set_line_join(cr, CAIRO_LINE_JOIN_MITER);
 
-  gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL);
-
-  XSetFunction (gstroke_disp, gstroke_gc, GXinvert);
-
-  XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr);
+	point = (p_point)iter->data;
+	iter = iter->next;
+	cairo_move_to(cr, point->x, point->y);
 
-  XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid,
-                      CapButt, JoinMiter);
-  XMapRaised (gstroke_disp, gstroke_window);
+	while (iter) {
+		point = (p_point)iter->data;
+		iter = iter->next;
 
-#if 0
-  /*FIXME: is this call really needed? If yes, does it need the real
-    argc and argv? */
-  hints.flags = PPosition | PSize;
-  XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL,
-                          (Pixmap)NULL, NULL, 0, &hints);
+		cairo_line_to(cr, point->x, point->y);
+	}
 
+	cairo_stroke(cr);
 
-  /* Receive the close window client message */
-  {
-    /* FIXME: is this really needed? If yes, something should be done
-       with wmdelete...*/
-    Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW",
-                                 False);
-    XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True);
-  }
-#endif
+	cairo_restore(cr);
+
+	return FALSE;
 }

mercurial