// build by gcc -o example-popup -g -O0 example-popup.c `pkg-config --libs --cflags gtk+-3.0`

#include <gtk/gtk.h>

#define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
                             GDK_BUTTON2_MASK | \
                             GDK_BUTTON3_MASK | \
                             GDK_BUTTON4_MASK | \
                             GDK_BUTTON5_MASK)

static gboolean pressed = FALSE;

static void
draw_tooltip_cb (GtkWidget *widget,
                 cairo_t   *cr,
                 gpointer   data)
{
    cairo_rectangle (cr, 0, 0, 100, 100);
    cairo_set_source_rgb (cr, 1, 0, 0);
    cairo_fill (cr);

    cairo_t *cr3 = gdk_cairo_create (gtk_widget_get_window (widget));
    cairo_rectangle (cr3, 0, 0, 70, 70);
    cairo_set_source_rgb (cr3, 0, 0, 1);
    cairo_fill (cr3);
}

/* Another callback */
static void
destroy (GtkWidget *widget, 
         gpointer data)
{
    gtk_main_quit ();
}

static GdkDevice *
current_pointer (GtkWidget *widget)
{
    GdkDevice *pointer, *device;
    GdkDisplay *display;

    display = gtk_widget_get_display (widget);
    device = gtk_get_current_event_device ();
    if (device && gdk_device_get_display (device) != display)
      device = NULL;

    if (device == NULL)
      {
        GdkDeviceManager *device_manager;
        GList *devices;

        device_manager = gdk_display_get_device_manager (display);
        devices = gdk_device_manager_list_devices (device_manager,
                                                   GDK_DEVICE_TYPE_MASTER);

        device = devices->data;
        g_list_free (devices);
      }

    if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
      {
        pointer = gdk_device_get_associated_device (device);
      }
    else
      {
        pointer = device;
      }

    return pointer;
}

static gboolean
handle_focus_in (GtkWidget *widget,
                 GdkEvent  *event,
                 gpointer   user_data)
{
    g_message ("Focus-in on widget %p (\"%s\")", widget, gtk_widget_get_name (widget));

    return FALSE;
}

static gboolean
handle_focus_out (GtkWidget *widget,
                  GdkEvent  *event,
                  gpointer   user_data)
{
    g_message ("Focus-out on widget %p (\"%s\")", widget, gtk_widget_get_name (widget));

    return FALSE;
}

static GtkWidget *
show_popup (GtkWidget *parent,
            guint32 time,
            int x,
            int y)
{
    GtkWidget *popup;
    GdkDevice *pointer;

    popup = gtk_window_new (GTK_WINDOW_POPUP);

    gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (parent)),
                                 GTK_WINDOW (popup));
    gtk_window_set_transient_for (GTK_WINDOW (popup), GTK_WINDOW (parent));

    gtk_widget_realize (popup);
    gtk_window_set_type_hint (GTK_WINDOW (popup), GDK_WINDOW_TYPE_HINT_POPUP_MENU);

    pointer = current_pointer (popup);
    if (pointer)
      {
        gdk_device_grab (pointer,
                         gtk_widget_get_window (popup),
                         GDK_OWNERSHIP_NONE,
                         TRUE,
                         GDK_ANY_BUTTON_MASK,
                         NULL,
                         time);
        g_object_set_data (G_OBJECT (popup), "grabbed-device", pointer);
      }

    gtk_window_move (GTK_WINDOW (popup), x, y);
    gtk_window_resize (GTK_WINDOW (popup), 250, 250);
    gtk_widget_show_now (GTK_WIDGET (popup));

    return popup;
}

static gboolean
button_press (GtkWidget      *widget,
              GdkEventButton *event,
              GtkWidget      *window)
{
    GtkWidget *popup;

    if (pressed)
      return FALSE;

    pressed = TRUE;
    g_message ("Press");
    popup = show_popup (window, event->time, event->x, event->y);
    g_object_set_data (G_OBJECT (window), "popup-window", popup);
    g_signal_connect (popup, "focus-in-event", G_CALLBACK (handle_focus_in), popup);
    g_signal_connect (popup, "focus-out-event", G_CALLBACK (handle_focus_out), popup);

    return FALSE;
}

static gboolean
button_release (GtkWidget      *widget,
                GdkEventButton *event,
                GtkWidget      *window)
{
    GtkWidget *popup;
    GdkDevice *pointer;

    if (!pressed)
      return FALSE;

    pressed = FALSE;
    g_message ("Release");
    popup = GTK_WIDGET (g_object_get_data (G_OBJECT (window), "popup-window"));
    if (!popup)
      {
        g_warning ("Cannot retrieve popup window");
        return FALSE;
      }
    gtk_widget_hide (popup);

    pointer = GDK_DEVICE(g_object_get_data (G_OBJECT (popup), "grabbed-device"));
    if (pointer)
      gdk_device_ungrab (pointer, event->time);

    gtk_widget_destroy (popup);

    return FALSE;
}

gint
main (int   argc, 
      char *argv[])
{
    GtkWidget *window;
    GtkWidget *container;
    GtkWidget *drawing;
    GtkWidget *popup;

    gtk_init (&argc, &argv);

    /* create a new window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    container = gtk_overlay_new ();
    gtk_container_add (GTK_CONTAINER (window), container);

    drawing = gtk_drawing_area_new ();
    gtk_container_add (GTK_CONTAINER (container), drawing);

    gtk_window_move (GTK_WINDOW (window), 20, 20);      /* <== Won't work on Wayland on toplevels */
    gtk_window_resize (GTK_WINDOW (window), 200, 200);

    g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
    g_signal_connect (window, "button-press-event", G_CALLBACK (button_press), window);
    g_signal_connect (window, "button-release-event", G_CALLBACK (button_release), window);
    g_signal_connect (window, "focus-in-event", G_CALLBACK (handle_focus_in), window);
    g_signal_connect (window, "focus-out-event", G_CALLBACK (handle_focus_out), window);
    g_signal_connect (drawing, "draw", G_CALLBACK (draw_tooltip_cb), NULL);

    gtk_widget_show_all (GTK_WIDGET (window));

    gtk_main ();

    return 0;
}
