/* GDK - The GIMP Drawing Kit
 * Copyright (C) 2009 Carlos Garnacho <carlosg@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include "gdkinternals.h"
#include "gdkprivate-wayland.h"

typedef struct _GdkWaylandEventSource {
  GSource source;
  GPollFD pfd;
  uint32_t mask;
  GdkDisplay *display;
} GdkWaylandEventSource;

static GList *event_sources = NULL;

static gboolean
gdk_event_source_prepare(GSource *base, gint *timeout)
{
  GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
  GdkDisplayWayland *display = (GdkDisplayWayland *) source->display;

  *timeout = -1;

  /* We have to add/remove the GPollFD if we want to update our
   * poll event mask dynamically.  Instead, let's just flush all
   * write on idle instead, which is what this amounts to. */

  if (_gdk_event_queue_find_first (source->display) != NULL)
    return TRUE;

  while (source->mask & WL_DISPLAY_WRITABLE)
    wl_display_iterate(display->wl_display, WL_DISPLAY_WRITABLE);

  return FALSE;
}

static gboolean
gdk_event_source_check(GSource *base)
{
  GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;

  return _gdk_event_queue_find_first (source->display) != NULL ||
    source->pfd.revents;
}

static gboolean
gdk_event_source_dispatch(GSource *base,
			  GSourceFunc callback,
			  gpointer data)
{
  GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
  GdkDisplay *display = source->display;
  GdkEvent *event;

  GDK_THREADS_ENTER ();

  event = gdk_display_get_event (display);

  if (event)
    {
      _gdk_event_emit (event);

      gdk_event_free (event);
    }

  GDK_THREADS_LEAVE ();

  return TRUE;
}

static void
gdk_event_source_finalize (GSource *source)
{
  event_sources = g_list_remove (event_sources, source);
}

static GSourceFuncs wl_glib_source_funcs = {
  gdk_event_source_prepare,
  gdk_event_source_check,
  gdk_event_source_dispatch,
  gdk_event_source_finalize
};

static int
gdk_event_source_update(uint32_t mask, void *data)
{
  GdkWaylandEventSource *source = data;

  source->mask = mask;

  return 0;
}

void
_gdk_wayland_display_deliver_event (GdkDisplay *display, GdkEvent *event)
{
  GList *node;
  static int serial;

  node = _gdk_event_queue_append (display, event);
  _gdk_windowing_got_event (display, node, event, serial++);
}

GSource *
_gdk_wayland_display_event_source_new (GdkDisplay *display)
{
  GSource *source;
  GdkWaylandEventSource *wl_source;
  GdkDisplayWayland *display_wayland;
  char *name;

  source = g_source_new (&wl_glib_source_funcs,
			 sizeof (GdkWaylandEventSource));
  name = g_strdup_printf ("GDK Wayland Event source (%s)", "display name");
  g_source_set_name (source, name);
  g_free (name);
  wl_source = (GdkWaylandEventSource *) source;

  display_wayland = GDK_DISPLAY_WAYLAND (display);
  wl_source->display = display;
  wl_source->pfd.fd = wl_display_get_fd(display_wayland->wl_display,
					gdk_event_source_update, source);
  wl_source->pfd.events = G_IO_IN | G_IO_ERR;
  g_source_add_poll(source, &wl_source->pfd);

  g_source_set_priority (source, GDK_PRIORITY_EVENTS);
  g_source_set_can_recurse (source, TRUE);
  g_source_attach (source, NULL);

  event_sources = g_list_prepend (event_sources, source);

  return source;
}

void
_gdk_wayland_display_flush (GdkDisplay *display, GSource *source)
{
  GdkWaylandEventSource *wayland_source = (GdkWaylandEventSource *) source;

  while (wayland_source->mask & WL_DISPLAY_WRITABLE)
    wl_display_iterate(GDK_DISPLAY_WAYLAND (display)->wl_display,
		       WL_DISPLAY_WRITABLE);
}

void
_gdk_wayland_display_queue_events (GdkDisplay *display)
{
  GdkDisplayWayland *display_wayland;
  GdkWaylandEventSource *source;

  display_wayland = GDK_DISPLAY_WAYLAND (display);
  source = (GdkWaylandEventSource *) display_wayland->event_source;

  if (source->pfd.revents)
    {
      wl_display_iterate(display_wayland->wl_display, WL_DISPLAY_READABLE);
      source->pfd.revents = 0;
    }
}
