#include <gtk/gtk.h>

static GdkPixbuf *screenshot = NULL;
static GdkPixbuf *modified = NULL;
static float factor = 1.0;
static float delta = 0.1;

static GtkWidget *monitor_window = NULL;
static GtkWidget *monitor_screenshot = NULL;
static GtkWidget *monitor_modified = NULL;

static void
make_monitor_window (void)
{
  if (monitor_window == NULL)
    {
      GtkWidget *hbox;
      
      monitor_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      gtk_window_set_title (GTK_WINDOW (monitor_window),
                            "Screenshot, and modified version");
      g_signal_connect (monitor_window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
      
      hbox = gtk_hbox_new (0, 0); /* one of the zeroes is false, who remembers */
      
      monitor_screenshot = gtk_image_new();
      monitor_modified = gtk_image_new();
      gtk_container_add (GTK_CONTAINER(monitor_window), hbox);

      gtk_box_pack_start (GTK_BOX(hbox), monitor_screenshot, TRUE, FALSE, 0);
      gtk_box_pack_end (GTK_BOX(hbox), monitor_modified, TRUE, FALSE, 0);
      
      gtk_widget_show_all (monitor_window);
    }
}

static gboolean
expose_handler (GtkWidget      *widget,
                GdkEventExpose *event,
                void           *data)
{
  int w, h;
  
  make_monitor_window ();
  
  if (screenshot != NULL)
    {
      g_object_unref (screenshot);
      g_object_unref (modified);
    }

  gdk_drawable_get_size (widget->window, &w, &h);
  
  screenshot = gdk_pixbuf_get_from_drawable (NULL,
                                             widget->window,
                                             NULL,
                                             0, 0,
                                             0, 0,
                                             w, h);

  modified = gdk_pixbuf_copy (screenshot);
  
  gtk_image_set_from_pixbuf (GTK_IMAGE (monitor_screenshot), screenshot);
  gtk_image_set_from_pixbuf (GTK_IMAGE (monitor_modified), modified);
  
  return FALSE;
}

static void
modify_pixbuf (GdkPixbuf *original,
               GdkPixbuf *destination,
               float      factor)
{
  const unsigned char *src = gdk_pixbuf_get_pixels (original);
  const unsigned char *src_end;
  unsigned char *dest = gdk_pixbuf_get_pixels (destination);
  gboolean has_alpha = gdk_pixbuf_get_has_alpha (original);
  
  src_end = src + (gdk_pixbuf_get_height (original) *
                   gdk_pixbuf_get_rowstride (original));
  
  while (src != src_end)
    {
      int c;
#define CLAMP_UCHAR(v) (CLAMP (v, 0, 255))
      
      c = *src++ * factor;
      *dest++ = CLAMP_UCHAR(c);
      c = *src++ * factor;
      *dest++ = CLAMP_UCHAR(c);
      c = *src++ * factor;
      *dest++ = CLAMP_UCHAR(c);
      
      if (has_alpha)
        {
          ++src;
          ++dest;
        }
    }
}

static gboolean
glow_timeout (void *data)
{
  GtkWidget *widget = GTK_WIDGET (data);

  if (modified == NULL)
    return TRUE;

  if ((delta > 0.0 && factor > 1.4) ||
      (delta < 0.0 && factor < 0.7))
    delta = - delta;

  factor += delta;

  modify_pixbuf (screenshot, modified, factor);
  
  gdk_draw_pixbuf (widget->window,
                   widget->style->fg_gc[GTK_STATE_NORMAL],
                   modified,
                   0, 0, 0, 0,
                   gdk_pixbuf_get_width (modified),
                   gdk_pixbuf_get_height (modified),
                   GDK_RGB_DITHER_NORMAL,
                   0, 0);

  if (monitor_modified)
    {      
      gtk_widget_queue_draw (monitor_modified);
      gdk_window_process_updates (monitor_modified->window, TRUE);
    }
  
  return TRUE;
}

int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *button;

  gtk_init (&argc, &argv);
  
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "A button");
  
  button = gtk_button_new_from_stock (GTK_STOCK_OPEN);

  gtk_container_add (GTK_CONTAINER (window), button);

  g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
  
  g_signal_connect_after (button, "expose_event",
                          G_CALLBACK (expose_handler),
                          NULL);

  g_timeout_add (50, glow_timeout, window);

  gtk_widget_show_all (window);
  
  gtk_main ();

  return 0;
}
