pidgin/plugins/gestures/stroke-draw.c

Wed, 25 Oct 2023 20:26:35 -0500

author
Elliott Sales de Andrade <quantum.analyst@gmail.com>
date
Wed, 25 Oct 2023 20:26:35 -0500
changeset 42410
563e7a17c220
parent 42246
3230f1a1fb8f
child 43090
a99e622e89ee
permissions
-rw-r--r--

Fix possible clash of config headers

Both GLib and GStreamer have a config file called `config.h` and use the `HAVE_CONFIG_H` macro to conditionally include them.

Normally, this works out fine, but if they are subprojects, we may accidentally trigger including a different `config.h`. This is normally harmless, but it causes a bunch of warnings because we define `GETTEXT_PACKAGE` on the command-line, and it won't match the one in a different project.

However, we only include this file in source files, not headers, and it's always created, so there's no need to conditionally include it or define the macro. We can also rename it so it can't clash with other instances as well.

Testing Done:
Compiled with subprojects and stopped seeing many warnings about `GETTEXT_PACKAGE` and other generic variables being re-defined due to GStreamer's `config.h`.

Reviewed at https://reviews.imfreedom.org/r/2707/

/*
  GNOME stroke implementation
  Copyright (c) 2000, 2001 Dan Nicolaescu
  See the file COPYING for distribution information.
*/

#include "purpleconfig.h"

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "gstroke.h"
#include "gstroke-internal.h"

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 int mouse_button = 2;
static gboolean draw_strokes = FALSE;

#define GSTROKE_TIMEOUT_DURATION 10

#define GSTROKE_SIGNALS "gstroke_signals"

struct gstroke_func_and_data {
	void (*func)(GtkWidget *, void *);
	gpointer data;
};


/*FIXME: maybe it's better to just make 2 static variables, not a
  structure */
struct mouse_position {
	struct s_point last_point;
	gboolean invalid;
};


static struct mouse_position last_mouse_position;
static guint timer_id;

static void gstroke_execute (GtkWidget *widget, const gchar *name);

static void
record_stroke_segment(GtkWidget *widget)
{
	gint x, y;
	struct gstroke_metrics *metrics;
	GdkSeat *seat;
	GdkDevice *dev;

	g_return_if_fail(widget != NULL);

	seat = gdk_display_get_default_seat(gtk_widget_get_display(widget));
	dev = gdk_seat_get_pointer(seat);
	gdk_window_get_device_position(gtk_widget_get_window(widget),
		dev, &x, &y, NULL);

	last_mouse_position.invalid = FALSE;

	if (last_mouse_position.last_point.x != x ||
		last_mouse_position.last_point.y != y)
	{
		last_mouse_position.last_point.x = x;
		last_mouse_position.last_point.y = y;
		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
gstroke_timeout (gpointer data)
{
	GtkWidget *widget;

	g_return_val_if_fail(data != NULL, FALSE);

	widget = GTK_WIDGET (data);
	record_stroke_segment (widget);

	return TRUE;
}

static void
gstroke_cancel(GtkWidget *widget, GdkEvent *event)
{
	last_mouse_position.invalid = TRUE;

	g_clear_handle_id(&timer_id, g_source_remove);

	if (event != NULL) {
		gdk_seat_ungrab(gdk_event_get_seat(event));
	}

	if (gstroke_draw_strokes()) {
		gtk_widget_queue_draw(widget);
	}
}

static gint
process_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
  static GtkWidget *original_widget = NULL;
  static GdkCursor *cursor = NULL;

  switch (event->type) {
    case GDK_BUTTON_PRESS:
		if (event->button.button != gstroke_get_mouse_button()) {
			/* Similar to the bug below catch when any other button is
			 * clicked after the middle button is clicked (but possibly
			 * not released)
			 */
				gstroke_cancel(widget, event);
			original_widget = NULL;
			break;
		}

      original_widget = widget; /* remember the widget where
                                   the stroke started */

      record_stroke_segment (widget);

	  if (cursor == NULL) {
		  GdkDisplay *display = gtk_widget_get_display(widget);
		  cursor = gdk_cursor_new_for_display(display, GDK_PENCIL);
	  }

      gdk_seat_grab(gdk_event_get_seat(event), gtk_widget_get_window(widget),
                    GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, event,
                    NULL, NULL);
      timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION,
				  gstroke_timeout, widget);
      return TRUE;

    case GDK_BUTTON_RELEASE:
      if ((event->button.button != gstroke_get_mouse_button())
	  || (original_widget == NULL)) {

		/* Nice bug when you hold down one button and press another. */
		/* We'll just cancel the gesture instead. */
				gstroke_cancel(widget, event);
		original_widget = NULL;
		break;
	  }

      last_mouse_position.invalid = TRUE;
      original_widget = NULL;
      g_clear_handle_id(&timer_id, g_source_remove);
	  gdk_seat_ungrab(gdk_event_get_seat(event));

			{
				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()) {
					gtk_widget_queue_draw(widget);
		}

				_gstroke_canonical(result, metrics);
				gstroke_execute(history, result);
			}
      return FALSE;

    default:
      break;
  }

  return FALSE;
}

void
gstroke_set_draw_strokes(gboolean draw)
{
	draw_strokes = draw;
}

gboolean
gstroke_draw_strokes(void)
{
	return draw_strokes;
}

void
gstroke_set_mouse_button(gint button)
{
	mouse_button = button;
}

guint
gstroke_get_mouse_button(void)
{
	return mouse_button;
}

void
gstroke_enable (GtkWidget *widget)
{
	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;

		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_set_visible(event, TRUE);

		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);

		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)
{
	GtkWidget *event = gtk_widget_get_parent(widget);

	g_return_if_fail(GTK_IS_EVENT_BOX(event));

	g_signal_handlers_disconnect_by_func(event, process_event, widget);
	g_signal_handlers_disconnect_by_func(event, gstroke_draw_cb, NULL);
}

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);

	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
gstroke_execute (GtkWidget *widget, const gchar *name)
{

  GHashTable *hash_table =
    (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);

#if 0
  purple_debug_misc("gestures", "gstroke %s", name);
#endif

  if (hash_table)
    {
      struct gstroke_func_and_data *fd =
	(struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name);
      if (fd)
	(*fd->func)(widget, fd->data);
    }
}

void
gstroke_cleanup (GtkWidget *widget)
{
	struct gstroke_metrics *metrics;
	GHashTable *hash_table = (GHashTable *)g_object_steal_data(G_OBJECT(widget),
	                                                           GSTROKE_SIGNALS);
	g_clear_pointer(&hash_table, g_hash_table_destroy);

	metrics = (struct gstroke_metrics *)g_object_steal_data(G_OBJECT(widget),
	                                                        GSTROKE_METRICS);
	g_free(metrics);
}

static gboolean
gstroke_draw_cb(GtkWidget *widget, cairo_t *cr,
                G_GNUC_UNUSED gpointer user_data)
{
	struct gstroke_metrics *metrics =
	        (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
	                                                    GSTROKE_METRICS);
	GSList *iter = NULL;
	p_point point;

	if (last_mouse_position.invalid) {
		return FALSE;
	}

	if (!metrics) {
		return FALSE;
	}

	iter = metrics->pointList;
	if (!iter) {
		return FALSE;
	}

	cairo_save(cr);

	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);

	point = (p_point)iter->data;
	iter = iter->next;
	cairo_move_to(cr, point->x, point->y);

	while (iter) {
		point = (p_point)iter->data;
		iter = iter->next;

		cairo_line_to(cr, point->x, point->y);
	}

	cairo_stroke(cr);

	cairo_restore(cr);

	return FALSE;
}

mercurial