/* GDK - The GIMP Drawing Kit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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 <curses.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "gdk.h"
#include "gdkx.h"
#include "gdkprivate.h"
#include "gdkinput.h"
#include "gdkkeysyms.h"

#if HAVE_CONFIG_H
#  include <config.h>
#  if STDC_HEADERS
#    include <string.h>
#  endif
#endif

#include <ctype.h>

GdkWindow *fg_window = NULL;

typedef struct _GdkIOClosure GdkIOClosure;

#define DOUBLE_CLICK_TIME      250
#define TRIPLE_CLICK_TIME      500
#define DOUBLE_CLICK_DIST      5
#define TRIPLE_CLICK_DIST      5

typedef enum {
  /* Following flag is set for events on the event queue during
   * translation and cleared afterwards.
   */
  GDK_EVENT_PENDING = 1 << 0
} GdkEventFlags;

struct _GdkIOClosure {
  GdkInputFunction function;
  GdkInputCondition condition;
  GdkDestroyNotify notify;
  gpointer data;
};

typedef struct _GdkEventPrivate {
  GdkEvent event;
  guint    flags;
} GdkEventPrivate;

/* 
 * Private function declarations
 */

#if 0
static gint	 gdk_event_apply_filters (XEvent *xevent,
					  GdkEvent *event,
					  GList *filters);
static gint	 gdk_event_translate	 (GdkEvent *event, 
					  XEvent   *xevent);
static Bool	 gdk_event_get_type	(Display      *display, 
					 XEvent	      *xevent, 
					 XPointer      arg);
#endif
static void      gdk_events_queue       (void);
static GdkEvent *gdk_event_unqueue      (void);

static gboolean  gdk_event_prepare      (gpointer  source_data, 
				 	 GTimeVal *current_time,
					 gint     *timeout);
static gboolean  gdk_event_check        (gpointer  source_data,
				 	 GTimeVal *current_time);
static gboolean  gdk_event_dispatch     (gpointer  source_data,
					 GTimeVal *current_time,
					 gpointer  user_data);

GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev,
					 GdkEvent  *event,
					 gpointer   data);

static int something_ready (void);


/* Private variable declarations
 */

static int connection_number = 0;	    /* The file descriptor number of our
					     *	connection to the X server. This
					     *	is used so that we may determine
					     *	when events are pending by using
					     *	the "select" system call.
					     */
static guint32 button_click_time[2];	    /* The last 2 button click times. Used
					     *	to determine if the latest button click
					     *	is part of a double or triple click.
					     */
static GdkWindow *button_window[2];	    /* The last 2 windows to receive button presses.
					     *	Also used to determine if the latest button
					     *	click is part of a double or triple click.
					     */
static guint button_number[2];		    /* The last 2 buttons to be pressed.
					     */
static GdkEventFunc   event_func;	    /* Callback for events */
static gpointer       event_data;
static GDestroyNotify event_notify;

/* FIFO's for event queue, and for events put back using
 * gdk_event_put().
 */
static GList *queued_events = NULL;
static GList *queued_tail = NULL;

static GSourceFuncs event_funcs = {
  gdk_event_prepare,
  gdk_event_check,
  gdk_event_dispatch,
  (GDestroyNotify)g_free
};

GPollFD event_poll_fd;

/*********************************************
 * Functions for maintaining the event queue *
 *********************************************/

/*************************************************************
 * gdk_event_queue_find_first:
 *     Find the first event on the queue that is not still
 *     being filled in.
 *   arguments:
 *     
 *   results:
 *     Pointer to the list node for that event, or NULL
 *************************************************************/

static GList *
gdk_event_queue_find_first (void)
{
  GList *tmp_list = queued_events;

  while (tmp_list)
    {
      GdkEventPrivate *event = tmp_list->data;
      if (!(event->flags & GDK_EVENT_PENDING))
	return tmp_list;

      tmp_list = g_list_next (tmp_list);
    }

  return NULL;
}

/*************************************************************
 * gdk_event_queue_remove_link:
 *     Remove a specified list node from the event queue.
 *   arguments:
 *     node: Node to remove.
 *   results:
 *************************************************************/

static void
gdk_event_queue_remove_link (GList *node)
{
  if (node->prev)
    node->prev->next = node->next;
  else
    queued_events = node->next;
  
  if (node->next)
    node->next->prev = node->prev;
  else
    queued_tail = node->prev;
  
}

/*************************************************************
 * gdk_event_queue_append:
 *     Append an event onto the tail of the event queue.
 *   arguments:
 *     event: Event to append.
 *   results:
 *************************************************************/

static void
gdk_event_queue_append (GdkEvent *event)
{
  queued_tail = g_list_append(queued_tail, event);
  
  if (!queued_events)
    queued_events = queued_tail;
  else
    queued_tail = queued_tail->next;
}

void 
gdk_events_init (void)
{
  connection_number = 0;
  GDK_NOTE (MISC,
	    g_message ("connection number: %d", connection_number));

  g_source_add (GDK_PRIORITY_EVENTS, TRUE, &event_funcs, NULL, NULL, NULL);

  event_poll_fd.fd = connection_number;
  event_poll_fd.events = G_IO_IN;
  
  g_main_add_poll (&event_poll_fd, GDK_PRIORITY_EVENTS);

  button_click_time[0] = 0;
  button_click_time[1] = 0;
  button_window[0] = NULL;
  button_window[1] = NULL;
  button_number[0] = -1;
  button_number[1] = -1;

#if 0
  gdk_add_client_message_filter (gdk_wm_protocols, 
				 gdk_wm_protocols_filter, NULL);
#endif
}

/*
 *--------------------------------------------------------------
 * gdk_events_pending
 *
 *   Returns if events are pending on the queue.
 *
 * Arguments:
 *
 * Results:
 *   Returns TRUE if events are pending
 *
 * Side effects:
 *
 *--------------------------------------------------------------
 */

int gdk_screen_dirty = 1;

gboolean
gdk_events_pending (void)
{
  return (gdk_event_queue_find_first() || something_ready());
}

/*
 *--------------------------------------------------------------
 * gdk_event_get_graphics_expose
 *
 *   Waits for a GraphicsExpose or NoExpose event
 *
 * Arguments:
 *
 * Results: 
 *   For GraphicsExpose events, returns a pointer to the event
 *   converted into a GdkEvent Otherwise, returns NULL.
 *
 * Side effects:
 *
 *-------------------------------------------------------------- */

GdkEvent *
gdk_event_get_graphics_expose (GdkWindow *window)
{
  return NULL;	
}

/*************************************************************
 * gdk_event_handler_set:
 *     
 *   arguments:
 *     func: Callback function to be called for each event.
 *     data: Data supplied to the function
 *     notify: function called when function is no longer needed
 * 
 *   results:
 *************************************************************/

void 
gdk_event_handler_set (GdkEventFunc   func,
		       gpointer       data,
		       GDestroyNotify notify)
{
  if (event_func && event_notify)
    (*event_notify) (event_data);

  event_func = func;
  event_data = data;
  event_notify = notify;
}

/*
 *--------------------------------------------------------------
 * gdk_event_get
 *
 *   Gets the next event.
 *
 * Arguments:
 *
 * Results:
 *   If an event was received that we care about, returns 
 *   a pointer to that event, to be freed with gdk_event_free.
 *   Otherwise, returns NULL. This function will also return
 *   before an event is received if the timeout interval
 *   runs out.
 *
 * Side effects:
 *
 *--------------------------------------------------------------
 */

GdkEvent *
gdk_event_get (void)
{
  gdk_events_queue();

  return gdk_event_unqueue();
}

/*
 *--------------------------------------------------------------
 * gdk_event_peek
 *
 *   Gets the next event.
 *
 * Arguments:
 *
 * Results:
 *   If an event is waiting that we care about, returns 
 *   a copy of that event, but does not remove it from
 *   the queue. The pointer is to be freed with gdk_event_free.
 *   Otherwise, returns NULL.
 *
 * Side effects:
 *
 *--------------------------------------------------------------
 */

GdkEvent *
gdk_event_peek (void)
{
  GList *tmp_list;

  tmp_list = gdk_event_queue_find_first ();
  
  if (tmp_list)
    return gdk_event_copy (tmp_list->data);
  else
    return NULL;
}

void
gdk_event_put (GdkEvent *event)
{
  GdkEvent *new_event;
  
  g_return_if_fail (event != NULL);
  
  new_event = gdk_event_copy (event);

  gdk_event_queue_append (new_event);
}

/*
 *--------------------------------------------------------------
 * gdk_event_copy
 *
 *   Copy a event structure into new storage.
 *
 * Arguments:
 *   "event" is the event struct to copy.
 *
 * Results:
 *   A new event structure.  Free it with gdk_event_free.
 *
 * Side effects:
 *   The reference count of the window in the event is increased.
 *
 *--------------------------------------------------------------
 */

static GMemChunk *event_chunk;

GdkEvent*
gdk_event_new (void)
{
  GdkEventPrivate *new_event;
  
  if (event_chunk == NULL)
    event_chunk = g_mem_chunk_new ("events",
				   sizeof (GdkEventPrivate),
				   4096,
				   G_ALLOC_AND_FREE);
  
  new_event = g_chunk_new (GdkEventPrivate, event_chunk);
  new_event->flags = 0;
  
  return (GdkEvent *) new_event;
}

GdkEvent*
gdk_event_copy (GdkEvent *event)
{
  GdkEvent *new_event;
  
  g_return_val_if_fail (event != NULL, NULL);
  
  new_event = gdk_event_new ();
  
  *new_event = *event;
  gdk_window_ref (new_event->any.window);
  
  switch (event->any.type)
    {
    case GDK_KEY_PRESS:
    case GDK_KEY_RELEASE:
      new_event->key.string = g_strdup (event->key.string);
      break;
      
    case GDK_ENTER_NOTIFY:
    case GDK_LEAVE_NOTIFY:
      if (event->crossing.subwindow != NULL)
	gdk_window_ref (event->crossing.subwindow);
      break;
      
    case GDK_DRAG_ENTER:
    case GDK_DRAG_LEAVE:
    case GDK_DRAG_MOTION:
    case GDK_DRAG_STATUS:
    case GDK_DROP_START:
    case GDK_DROP_FINISHED:
      gdk_drag_context_ref (event->dnd.context);
      break;

      
    default:
      break;
    }
  
  return new_event;
}

/*
 *--------------------------------------------------------------
 * gdk_event_free
 *
 *   Free a event structure obtained from gdk_event_copy.  Do not use
 *   with other event structures.
 *
 * Arguments:
 *   "event" is the event struct to free.
 *
 * Results:
 *
 * Side effects:
 *   The reference count of the window in the event is decreased and
 *   might be freed, too.
 *
 *-------------------------------------------------------------- */

void
gdk_event_free (GdkEvent *event)
{
  g_assert (event_chunk != NULL);
  g_return_if_fail (event != NULL);
  
  if (event->any.window)
    gdk_window_unref (event->any.window);
  
  switch (event->any.type)
    {
    case GDK_KEY_PRESS:
    case GDK_KEY_RELEASE:
      g_free (event->key.string);
      break;
      
    case GDK_ENTER_NOTIFY:
    case GDK_LEAVE_NOTIFY:
      if (event->crossing.subwindow != NULL)
	gdk_window_unref (event->crossing.subwindow);
      break;
      
    case GDK_DRAG_ENTER:
    case GDK_DRAG_LEAVE:
    case GDK_DRAG_MOTION:
    case GDK_DRAG_STATUS:
    case GDK_DROP_START:
    case GDK_DROP_FINISHED:
      gdk_drag_context_unref (event->dnd.context);
      break;

      
    default:
      break;
    }
  
  g_mem_chunk_free (event_chunk, event);
}

/*
 *--------------------------------------------------------------
 * gdk_event_get_time:
 *    Get the timestamp from an event.
 *   arguments:
 *     event:
 *   results:
 *    The event's time stamp, if it has one, otherwise
 *    GDK_CURRENT_TIME.
 *--------------------------------------------------------------
 */

guint32
gdk_event_get_time (GdkEvent *event)
{
  if (event)
    switch (event->type)
      {
      case GDK_MOTION_NOTIFY:
	return event->motion.time;
      case GDK_BUTTON_PRESS:
      case GDK_2BUTTON_PRESS:
      case GDK_3BUTTON_PRESS:
      case GDK_BUTTON_RELEASE:
	return event->button.time;
      case GDK_KEY_PRESS:
      case GDK_KEY_RELEASE:
	return event->key.time;
      case GDK_ENTER_NOTIFY:
      case GDK_LEAVE_NOTIFY:
	return event->crossing.time;
      case GDK_PROPERTY_NOTIFY:
	return event->property.time;
      case GDK_SELECTION_CLEAR:
      case GDK_SELECTION_REQUEST:
      case GDK_SELECTION_NOTIFY:
	return event->selection.time;
      case GDK_PROXIMITY_IN:
      case GDK_PROXIMITY_OUT:
	return event->proximity.time;
      case GDK_DRAG_ENTER:
      case GDK_DRAG_LEAVE:
      case GDK_DRAG_MOTION:
      case GDK_DRAG_STATUS:
      case GDK_DROP_START:
      case GDK_DROP_FINISHED:
	return event->dnd.time;
      default:			/* use current time */
	break;
      }
  
  return GDK_CURRENT_TIME;
}

/*
 *--------------------------------------------------------------
 * gdk_set_show_events
 *
 *   Turns on/off the showing of events.
 *
 * Arguments:
 *   "show_events" is a boolean describing whether or
 *   not to show the events gdk receives.
 *
 * Results:
 *
 * Side effects:
 *   When "show_events" is TRUE, calls to "gdk_event_get"
 *   will output debugging informatin regarding the event
 *   received to stdout.
 *
 *--------------------------------------------------------------
 */

void
gdk_set_show_events (int show_events)
{
  if (show_events)
    gdk_debug_flags |= GDK_DEBUG_EVENTS;
  else
    gdk_debug_flags &= ~GDK_DEBUG_EVENTS;
}

gint
gdk_get_show_events (void)
{
  return gdk_debug_flags & GDK_DEBUG_EVENTS;
}

static void
gdk_io_destroy (gpointer data)
{
  GdkIOClosure *closure = data;

  if (closure->notify)
    closure->notify (closure->data);

  g_free (closure);
}

static gboolean  
gdk_io_invoke (GIOChannel   *source,
	       GIOCondition  condition,
	       gpointer      data)
{
  GdkIOClosure *closure = data;
  GdkInputCondition gdk_cond = 0;

  if (condition & (G_IO_IN | G_IO_PRI))
    gdk_cond |= GDK_INPUT_READ;
  if (condition & G_IO_OUT)
    gdk_cond |= GDK_INPUT_WRITE;
  if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
    gdk_cond |= GDK_INPUT_EXCEPTION;

  if (closure->condition & gdk_cond)
    closure->function (closure->data, g_io_channel_unix_get_fd (source), gdk_cond);

  return TRUE;
}

gint
gdk_input_add_full (gint	      source,
		    GdkInputCondition condition,
		    GdkInputFunction  function,
		    gpointer	      data,
		    GdkDestroyNotify  destroy)
{
  guint result;
  GdkIOClosure *closure = g_new (GdkIOClosure, 1);
  GIOChannel *channel;
  GIOCondition cond = 0;

  closure->function = function;
  closure->condition = condition;
  closure->notify = destroy;
  closure->data = data;

  if (condition & GDK_INPUT_READ)
    cond |= (G_IO_IN | G_IO_PRI);
  if (condition & GDK_INPUT_WRITE)
    cond |= G_IO_OUT;
  if (condition & GDK_INPUT_EXCEPTION)
    cond |= G_IO_ERR|G_IO_HUP|G_IO_NVAL;

  channel = g_io_channel_unix_new (source);
  result = g_io_add_watch_full   (channel, G_PRIORITY_DEFAULT, cond, 
				  gdk_io_invoke,
				  closure, gdk_io_destroy);
  g_io_channel_unref (channel);

  return result;
}

gint
gdk_input_add (gint		 source,
	       GdkInputCondition condition,
	       GdkInputFunction	 function,
	       gpointer		 data)
{
  return gdk_input_add_full (source, condition, function, data, NULL);
}

void
gdk_input_remove (gint tag)
{
  g_source_remove (tag);
}

static gint
gdk_event_apply_filters (void *xevent,
			 GdkEvent *event,
			 GList *filters)
{
  GdkEventFilter *filter;
  GList *tmp_list;
  GdkFilterReturn result;
  
  tmp_list = filters;
  
  while (tmp_list)
    {
      filter = (GdkEventFilter *)tmp_list->data;

      if (!filter->function) {
	g_warning( "filter function not good, continuing\n" );
	result = GDK_FILTER_CONTINUE;
      } else result = (*filter->function)(NULL, event, filter->data);
      if (result !=  GDK_FILTER_CONTINUE)
	return result;
      
      tmp_list = tmp_list->next;
    }
  
  return GDK_FILTER_CONTINUE;
}

GdkFilterReturn
gdk_wm_protocols_filter (GdkXEvent *xev,
		     GdkEvent  *event,
		     gpointer data)
{
  g_error( "Should never call this one" );
  return 0;
}

static int
something_ready (void)
{
  fd_set rfds;
  struct timeval tv;
  int retval;

  gdk_flush();
  /* Watch stdin (fd 0) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(0, &rfds);
  tv.tv_sec = 0;
  tv.tv_usec = 0;

  retval = select(1, &rfds, NULL, NULL, &tv);

  return retval;
}

#define NEITHER 3
int
gdk_event_queue (GdkEvent *event, GdkWindowPrivate *window_private)
{
  if (window_private && window_private->destroyed)
    return FALSE;
  else
    {
      GdkFilterReturn result;

      /* Check for filters for this window
       */
      result = gdk_event_apply_filters (NULL, event,
					window_private
					?window_private->filters
					:gdk_default_filters);
      
      if (result != GDK_FILTER_CONTINUE)
	{
	  return (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
	}
    }

  gdk_window_ref( (GdkWindow *) window_private );
  queued_tail = g_list_append(queued_tail, event);
  
  if (!queued_events)
    queued_events = queued_tail;
  else
    queued_tail = queued_tail->next;
  return NEITHER;
}

void
gdk_window_expose (GdkWindowPrivate *win)
{
  GList *tmp_list;
  GdkEvent *event;

  if (win->destroyed)
    return;

  if ((win != &gdk_root_parent) && (win->cwin)){
    wclear(win->cwin);
    event = gdk_event_new();
    event->expose.type = GDK_EXPOSE;
    event->expose.window = (GdkWindow *) win;
    event->expose.area.x = 0;
    event->expose.area.y = 0;
    event->expose.area.width = win->width;
    event->expose.area.height = win->height;
    event->expose.count = 0;
    gdk_event_queue(event, win);
  }

  tmp_list = win->children;
  while (tmp_list)
    {
      GdkWindowPrivate *child;
      child = (GdkWindowPrivate *)tmp_list->data;
      gdk_window_expose( child );
      tmp_list = tmp_list->next;
    }
}

static void
menu(void)
{
  GdkEvent *event;
  GdkWindowPrivate *private = (GdkWindowPrivate *) fg_window;

  gdk_statusline( "_L:redraw, window _Close ma_Ximize l_Ist _Next _Prev n_Op" );
  switch (getch()) {
  case 'n':
    if (!fg_window) {
      gdk_statusline_err( "No foreground window to work with." );
      break;
    }
    gdk_window_raise(fg_window);
    /* Fall through */
  case 'l':
  case 12:
    gdk_statusline( "Redrawing everything..." );
    gdk_set_fg_window();
    gdk_window_expose(&gdk_root_parent);
    break;
  case 'o':
  case ' ':
    gdk_statusline( "Ook, aborting." );
    break;
  case 'x':
    if (!fg_window) {
      gdk_statusline_err( "No foreground window to work with." );
      break;
    }
    gdk_window_move(fg_window, 0, 0);
    gdk_window_resize(fg_window, gdk_screen_width(), gdk_screen_height());
  case 'r':
    if (!fg_window) {
      gdk_statusline_err( "No foreground window to work with." );
      break;
    }
    event = gdk_event_new();
    event->configure.type = GDK_CONFIGURE;
    event->configure.window = fg_window;
    event->configure.width = private->width;
    event->configure.height = private->height;
    event->configure.x = private->x;
    event->configure.y = private->y;
    gdk_event_queue(event, fg_window);
    break;
  case 'c':
    if (!fg_window) {
      gdk_statusline_err( "No foreground window to work with." );
      break;
    }
    event = gdk_event_new();
    event->any.type = GDK_DESTROY;
    event->any.window = fg_window;
    gdk_event_queue(event, fg_window);
    break;
  default:
    gdk_statusline_err( "You pressed unknown key, exiting menu. Use _O or _S_P_C next time." );
  }
  gdk_set_fg_window();
}

static GdkWindow *
fill_keyboard_event (GdkEvent *event)
{
  char buf[2];
  int ch = getch();
  int state = 0;
  GdkWindow *win = kbd_window ? kbd_window : fg_window;

  if (ch == 27) {
    ch = getch();
    state |= GDK_MOD1_MASK;
  }

  if ((ch == ' ') && (state == GDK_MOD1_MASK)) {
    menu();
    return NULL;
  }

  /* ^L */
  if (ch == 12) {
    fprintf( stderr, "Redraw everyone..." ); 
    gdk_window_expose(&gdk_root_parent); 
    fprintf( stderr, "done\n" );
    ch = 0;	/* Hope it does no harm */
    return NULL;
  }

  buf[0]=ch;
  buf[1]=0;

  event->any.send_event = FALSE;
  event->key.type = GDK_KEY_PRESS;
  event->key.window = win;
  //  gdk_window_ref(win);

  fprintf( stderr, "Hooray, have character %d from curses, going to %s!\n", ch, "(sorry)" );

  event->key.time = GDK_CURRENT_TIME;
  event->key.state = state;
  event->key.string = g_strdup (buf);
  event->key.length = 1;
  event->key.send_event = 0;

  if ((ch > 0) && (ch < 32)) {
    event->key.state |= GDK_CONTROL_MASK;
  }
  if (isupper(ch))
    event->key.state |= GDK_SHIFT_MASK;

#define XLATE(b,a) case a: event->key.keyval = b; event->key.length = 0; event->key.string = NULL; event->key.state = 0; break;
  switch (ch) {
#include "gdkkeysyms.xlate.h"
  case 0: event->key.length = 0; break;
  default: event->key.keyval = ch + (event->key.state & GDK_CONTROL_MASK ? ('a'-1) : 0); break;
  }
  fprintf( stderr, "Sending keyboard event, %s/%d, keyval %x, state %d, window %p\n", 
	   event->key.string, event->key.length, event->key.keyval, event->key.state, win );
  return win;
}

static void
gdk_events_queue (void)
{
  GdkEvent *event;

  while (!gdk_event_queue_find_first() && something_ready())
    {
      GdkWindowPrivate *window_private;
      int i;

      event = gdk_event_new ();
      window_private = (GdkWindowPrivate *) fill_keyboard_event(event);

      if (!window_private)
	return;

      i = gdk_event_queue (event, window_private);
      if (i != NEITHER)
	return;
    }
}

static gboolean  
gdk_event_prepare (gpointer  source_data, 
		   GTimeVal *current_time,
		   gint     *timeout)
{
  gboolean retval;
  
  GDK_THREADS_ENTER ();

  *timeout = -1;

  retval = (gdk_event_queue_find_first () != NULL) || something_ready();

  GDK_THREADS_LEAVE ();

  return retval;
}

static gboolean  
gdk_event_check   (gpointer  source_data,
		   GTimeVal *current_time)
{
  gboolean retval;
  
  GDK_THREADS_ENTER ();

  if (event_poll_fd.revents & G_IO_IN)
    retval = (gdk_event_queue_find_first () != NULL) || something_ready();
  else
    retval = FALSE;

  GDK_THREADS_LEAVE ();

  return retval;
}

static GdkEvent *
gdk_event_unqueue (void)
{
  GdkEvent *event = NULL;
  GList *tmp_list;

  tmp_list = gdk_event_queue_find_first ();

  if (tmp_list)
    {
      event = tmp_list->data;
      gdk_event_queue_remove_link (tmp_list);
      g_list_free_1 (tmp_list);
    }

  return event;
}

static gboolean  
gdk_event_dispatch (gpointer  source_data,
		    GTimeVal *current_time,
		    gpointer  user_data)
{
  GdkEvent *event;
 
  GDK_THREADS_ENTER ();

  gdk_events_queue();
  event = gdk_event_unqueue();

  if (event)
    {
      if (event_func)
	(*event_func) (event, event_data);
      
      gdk_event_free (event);
    }
  
  GDK_THREADS_LEAVE ();

  return TRUE;
}

/* Sends a ClientMessage to all toplevel client windows */
gboolean
gdk_event_send_client_message (GdkEvent *event, guint32 xid)
{
  g_error( "event_send_client_message unimplemented" );
  return FALSE;
}

void
gdk_event_send_clientmessage_toall (GdkEvent *event)
{
  g_error( "event_send_client_message_toall unimplemented" );
}

