/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "wm.h"

/* our handler table */
typedef void (*event_handler_t)(XEvent *event);
static event_handler_t handlers[LASTEvent];

/* handler for DestroyNotify events */
static void event_destroy_notify(XEvent *event)
{
	XDestroyWindowEvent *e = &event->xdestroywindow;
	client_t *client;

	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
		return;
	if (e->window != client->window)
		return;

	if (!client->flags.internal)
		XRemoveFromSaveSet(display, client->window);

	plugin_window_death(client);

   log(2,"event_destroy_notify removing client from list\n");
	client_rm(client);
}

// bva
static void event_configure_notify(XEvent *event)
{
	XConfigureEvent *e = &event->xconfigure;
	client_t *client;

   //printf( "---------[ event.c :: event_configure_notify (%d) ]-------------\n",
   //        e->type );

	/* get associated client, the event must be for it's window */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
   {
      //  printf("\tno associated client\n");
      return;
   }

	/*
	 * notify plugins about the property event; plugins can use this
	 * be aware of client title changes, or clients changing hinting
	 * properties.  if a plugin is using the property change, and is
	 * certain noone else will be (i.e. a specific windowmanager hint
	 * protocol) it may return PLUGIN_USING, in which case we don't
	 * need to do anything else for this event.
	 */
    //    printf("\tlooking for plugins .....\n");


   if (plugin_configure_notify(client, e) == PLUGIN_USING)
   {
      return;
   }
}

/* handler for ConfigureRequest */
static void event_configure_request(XEvent *event)
{
	XConfigureRequestEvent *e = &event->xconfigurerequest;
	XWindowChanges wc;
	client_t *client;

   log_enter( " window 0x%x parent 0x%x mask: 0x%x",
              (int) e->window, (int) e->parent, (int) e->value_mask );

	/* now we only update the structure if the window has one */
	if( XFindContext(display, e->window, client_context, (XPointer *) &client) )
   {
		client = NULL;
   }

	/*
	 * we need to respond to ConfigureRequests of windows that
	 * haven't yet been mapped, and thus don't have client_t structs
	 * in our list.
	 */
	if (!client)
   {
      log( 4," + no client associated with window 0x%x\n", (int) e->window );
      wc.x = e->x;
      wc.y = e->y;
      wc.width = e->width;
      wc.height = e->height;
      wc.border_width = e->border_width;
      wc.stack_mode = e->detail;
      XConfigureWindow(display, e->window, e->value_mask, &wc);

      return;
	}

	/* the event should be for the client's window */
	assert(e->window == client->window);

   if( !client->flags.nofocus )
   {
      focus_setfocused(client);
   }

   /*
    * now for managed windows we do stuff to our managed
    * client structure.
    */
   if (e->value_mask & CWX)
   {
      client->x = e->x;
   }
   if (e->value_mask & CWY)
      client->y = e->y;
   if (e->value_mask & CWWidth)
      client->width = e->width;
   if (e->value_mask & CWHeight)
      client->height = e->height;

   /*
    * when a client moves itself, it may need
    * to change workspaces
    */
   client_gravitate(client);
   client_sizeframe(client);
   action_send_config(client);
   workspace_add_bypos(client->screen->desktop, client);
   plugin_geometry_change(client);

   log_exit( "" );
}

/* handler for MapRequest events */
static void event_map_request(XEvent *event)
{
	XMapRequestEvent *e = &event->xmaprequest;
	screen_t *screen = NULL;
	client_t *client = NULL;

   log_enter( "window 0x%x, parent: 0x%x send_event: %d type: %d",
              (int) e->window, (int) e->parent, e->send_event, e->type  );

	/*
	 * for some reason some broken clients (netscape) seem to do more
	 * than one map request on top window..., so make sure this window
	 * isn't already managed.
	 */
	if( XFindContext(display, e->window, client_context, (XPointer *) &client) == 0 )
   {
      log( 2, " + this isn't a new window" );

      /*  we have a client context .... */
      if( client->state == IconicState )
      {
         action_restore( client );
      }
      else
      {
         if( client->flags.tentatively_removed )
         {
            client_readd( client );
         }
      }

      return;
   }


	if( XFindContext(display, e->parent, root_context, (XPointer *) &screen) > 0 )
   {
      if( XFindContext(display, e->parent, client_context, (XPointer *) &client ) > 0 )
      {
         log( 2, "no client context either ...\n" );
      }
      log( 2, "no parent window context");

      return;
   }

	/*
	 * give plugins a chance to handle this window exclusively,
	 * if plugin_map_request returns PLUGIN_USING we don't managed
	 * in with client_add.
	 */
	if( plugin_map_request(screen, e) )
   {
		return;
   }

	client = client_add(screen, e->window, NULL, NULL);

	if (!client)
   {
		return;
   }

	client_map(client, 1);

   log_exit( "" );
}

/* handler for MapNotify events */
static void event_map_notify(XEvent *event)
{
	XMapEvent *e = &event->xmap;
	client_t *client;

   log( 0, "event_map_notify");

	/*
	 * look for the client context we created for it in event_map_request;
	 * and only continue if this is a notify from the client->window; if
	 * it's for the frame we don't care here.
	 */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
   {
      log( 2, "no context for window: %d", (int) e->window);
      return;
   }
	if (e->window != client->window)
   {
      log(2,"Not a client window event, most likely frame: %d, %d",
            (int) e->window, (int) client->window );
      return;
   }

	/* give focus to new windows (if the options say to) */
	if (options.focus_new && client->workspace)
   {
      log(2,"setting the focus ....");
      focus_setfocused(client);
   }
}


static int event_errors = 0;

int event_error_handler (Display* dpy, XErrorEvent* e)
{
	char buffer[100];

	XGetErrorText (dpy, e->error_code, buffer, 100);
	event_errors++;


	return (event_errors); /* ignored anyway */
}


Bool map_request_predicate ( Display *display, XEvent *event, XPointer arg )
{
   XMapRequestEvent *e = &event->xmaprequest;

   if( event->type == MapRequest && (Window) arg == e->window )
   {
      return 1; /* or set a 'stop' flag for the computation */
   }
   return 0;
}

int event_check_for_pending_map( Window window, Window new_parent )
{
   XEvent e;
   int ret = 0;

#if 0
   XFlush( display );   if( XCheckIfEvent( display, &e,  map_request_predicate,
                      (XPointer) window ) )
   {
      if( new_parent )
      {
         e.xmaprequest.parent = new_parent;
      }

      XSendEvent(display, window, 0, StructureNotifyMask, &e);

      return 1;
   }

   return 0;

#else

   grab_server( 1 );

   if( XCheckTypedWindowEvent(display, window, MapRequest, &e) )
   {
      while (XCheckTypedWindowEvent(display, new_parent, UnmapNotify, &e));

      XPutBackEvent(display, &e);
      ret = 1;
   }

   grab_server( 0 );

   return ret;

#endif
}

int event_check_for_pending_evt( Window window, int event, int purge, int putback )
{
   XEvent e;
   int ret = 0;

   grab_server( 1 );

   if( XCheckTypedWindowEvent(display, window, event, &e) )
   {
      printf( "found event\n");
      if( purge )
      {
         while (XCheckTypedWindowEvent(display, window, event, &e));
      }

      if( putback )
      {
         XPutBackEvent(display, &e);
      }
      ret = 1;
   }

   grab_server( 0 );

   return ret;
}





/* handler for UnmapNotify events */
static void event_unmap_notify(XEvent *event)
{
   XUnmapEvent *e = &event->xunmap;
   client_t *client;
   int ret;

   log_enter( " window: 0x%x", (int) e->window );


	/*
	 * handle unmap notify events on managed windows; we only are
	 * interested in the unmapnotify events generated when the
	 * client window (not the frame) unmaps; because that signals
	 * that the client should be unmanaged.  the only exception
	 * is when we get unmap events from a reparent (see below).
	 */
	if( XFindContext(display, e->window, client_context, (XPointer *) &client) )
   {
      log( 2, "no client context ... returning");
		return;
   }

   if( client->flags.tentatively_removed )
   {
      client->flags.tentatively_removed = 1;
      log( 2, "already unmapped\n" );
      return;
   }

   /* kludge alert: for some reason when switching from full screen to
      normal mode applications such as mplayer already have a map
      event on the X event queue when we get this unmap event. The
      problem is that we'll go ahead and process this unmap event and
      delete the client, etc. This would be ok, except the map event
      we haven't seen has the parent of the client->window as the
      frame (since we haven't done the reparent call at the bottom of
      this function). That client->window to client->frame parenting
      arrangement fails the root_context check in map_request. i.e. we
      won't re-map the window if it's parent doesn't have a root
      context, in order for that to work your parent must be the root
      window.

      So to solve this problem, we'll flush the event queue and then
      look for pending map events *before* processing this event. If
      we find a pending map event for this window, we'll modify it to
      have the root window as its parent and it will survive the
      re-map grilling.

      This is a nasty kludge, but I can't find a better way to do it
      without re-writing the way things are mapped to not care about
      the parent.
   */

   if( event_check_for_pending_evt( client->frame, MapRequest, 1, 1 ) )
   {
      log( 2, "pending map of frame detected, skipping unmap\n" );

      event_check_for_pending_evt( client->frame, UnmapNotify, 1, 0 );
      event_check_for_pending_evt( client->screen->root, UnmapNotify, 1, 0 );

      return;
   }


   // this most likely means you've iconified a window ....
	if( e->window != client->window )
   {
      log(2,"e->window != client->window, i.e. you've unmapped the frame");
      return;
   }

	/*
	 * see if the event came from reparenting
	 * (see screen.c, manage_existing_windows)
	 */
   if( client->flags.unmap_from_reparent )
   {
		client_map(client, 0);
		client->flags.unmap_from_reparent = 0;

      log( 2, "client->flags.unmap_from_reparent\n" );

		return;
	}

    // we've iconified, so don't kill the window off!
    if( client->state == IconicState )
    {
       log(2,"client->state == IconicState");
       return;
    }

	/*
	 * get rid of our client structure, reparent it to the root,
	 * and remove from our saveset
	 */
	if (client->state == NormalState)
   {
      XUnmapWindow(display, client->frame);
   }

   plugin_window_death(client);

	client_setstate(client, WithdrawnState);
	if (!client->flags.internal)
   {
		XRemoveFromSaveSet(display, client->window);
   }

   XSetErrorHandler (event_error_handler);

	ret = XReparentWindow( display, client->window, client->screen->root,
                          client->x, client->y);

   log( 4, " window: 0x%x off client list, parent is: 0x%x, ret is %d",
        (int) e->window, (int) client->screen->root, ret );

   // #warning temp .. client_rm only in destroy notify.
   //	client_rm(client);
   if( client->flags.tentatively_removed == 0 )
   {
      client_tentatively_rm( client );
   }

   log_exit( "" );
}


/* handler for ButtonPress events */
static void event_button_press(XEvent *event)
{
	XButtonEvent *e = &event->xbutton;
	client_t *client;
	screen_t *screen;
	decor_t *decor;

	/*
	 * if the click doesn't have a client context, check if it is
	 * a root window click; in which case notify plugins.
	 */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
   {
      if (!XFindContext(display, e->window, root_context, (XPointer *) &screen))
      {
         plugin_root_button(screen, e);
      }
   }
   /*
    * handle button presses on client frame window (for mouse_modifier
    * grabs), the client window to do focusing in click focus mode,
    * or client decoration.
    */
   if (e->window == client->frame)
   {
      if (options.focus == FOCUS_CLICK)
         focus_setfocused(client);

      /*
       * either do move/resize for mouse_modifier grabs, or in
       * click focus mode we will get events for initial clicks
       * in the click->frame to do a raise.
       *
       * XXX: the buttons that do which for mouse_modifier should
       * be configurable.
       */
      if ((e->state & ~(numlock_mask | scroll_mask | LockMask)) == options.mouse_modifier)
      {
         if (e->button == Button3)
         {
            action_resize(client);
         }
         else
         {
            action_move(client);
         }
      }
      else
      {
         XAllowEvents(display, ReplayPointer, CurrentTime);
         stacking_raise(client);
      }
   }
   else if (!XFindContext(display, e->window, decor_context, (XPointer *) &decor))
   {
      if (options.focus == FOCUS_CLICK)
      {
         focus_setfocused(client);
      }
      decor_handlepress(client, decor, e);
   }
}

/* handler for ButtonRelease events */
static void event_button_release(XEvent *event)
{
	XButtonEvent *e = &event->xbutton;
	screen_t *screen;


	/*
	 * the only clicks we are concerned with are clicks on
	 * a managed root window.
	 */
	if (XFindContext(display, e->window, root_context, (XPointer *) &screen))
    {
       return;
    }

	plugin_root_button(screen, e);

	/*
	 * switch workspace when clicking on the edges of the
	 * screen.
	 *
	 * XXX: this should likely be removed at some point...
	 */
	if (e->button == Button1)
    {
		if (e->y_root == 0) {
			if (workspace_viewport_move(screen, screen->desktop, 0, -1))
				XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
					e->x_root, screen->height - 1);
		} else if (e->y_root == screen->height - 1) {
			if (workspace_viewport_move(screen, screen->desktop, 0, 1))
				XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
					e->x_root, 0);
		} else if (e->x_root == 0) {
			if (workspace_viewport_move(screen, screen->desktop, -1, 0))
				XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
					screen->width - 1, e->y_root);
		} else if (e->x_root == screen->width - 1) {
			if (workspace_viewport_move(screen, screen->desktop, 1, 0))
				XWarpPointer(display, None, screen->root, 0, 0, 1, 1,
					0, e->y_root);
		}
	}
}


/* handler for KeyPress events */
static void event_key_press(XEvent *event)
{
	XKeyEvent *e = &event->xkey;
	screen_t *screen;

	/*
	 * handle dispatching key bindings; get the screen on which
	 * the event occured and pass it to the key_press handler.
	 */
	if (XFindContext(display, e->root, root_context, (XPointer *) &screen))
		return;
	keys_press(screen, e);
}

/* handler for Expose events */
static void event_expose(XEvent *event)
{
	XExposeEvent *e = &event->xexpose;
	client_t *client;
	decor_t *decor;

	/*
	 * handle exposures for client decoration.
	 */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
		return;
	if (XFindContext(display, e->window, decor_context, (XPointer *) &decor))
		return;
	decor_expose(client, decor, e);
}

/* handler for ClientMessage */
static void event_client_message(XEvent *event)
{
	XClientMessageEvent *e = &event->xclient;
	client_t *client;
   screen_t *screen;


   log( 0, "client message type: %d, data: %d",
        (int) e->message_type, (int)e->data.l[0] );

	if( XFindContext( display, e->window, client_context, (XPointer *) &client) > 0 )
   {
      log( 2, "not internal context");
      if ( XFindContext(display, e->window, root_context, (XPointer *) &screen) == 0 )
      {
         plugin_ext_client_message( screen, e );
      }
      else
      {
         log(2,"no root context ....");
      }

      // bva. we are getting here on external desktop changes ...
      return;
   }

	if (e->window != client->window)
   {
      return;
   }

   /*

   * currently, only handle state change requests
   * for iconification.
   */
	if ( e->message_type == WM_CHANGE_STATE &&
        e->format == 32 &&
        e->data.l[0] == IconicState &&
        client->state != IconicState )
   {
      log(2,"============================================================");
      log(2,"============================================================");
      log(2,"--------------[ iconifying the client .... ]----------------");
      log(2,"============================================================");
      log(2,"============================================================");

      action_iconify(client);
   }
}

/* handler for PropertyNotify */
static void event_property_notify(XEvent *event)
{
	XPropertyEvent *e = &event->xproperty;
	client_t *client;
	long supplied;
#ifdef I18N
	XTextProperty text_prop;
	char **cl;
	int n;
#endif

    log( -1, "event_property_notify");
    // bvaif( e->window == screen->root )
    // bva {
    // bva   printf("root window event\n");
    // bva }

	/* get associated client, the event must be for it's window */
	if( XFindContext(display, e->window, client_context, (XPointer *) &client) )
   {
      log( -1,"no associated client");
      return;
   }

	assert(e->window == client->window);

	/*
	 * notify plugins about the property event; plugins can use this be aware
	 * of client title changes, or clients changing hinting properties.  if a
	 * plugin is using the property change, and is certain noone else will be
	 * (i.e. a specific windowmanager hint protocol) it may return
	 * PLUGIN_USING, in which case we don't need to do anything else for this
	 * event.
	 */
    if( plugin_property_notify(client, e) == PLUGIN_USING )
    {
       return;
    }

	/*
	 * support changing the normal hints for a managed client window, and
	 * changing the client store name.
	 */
	if( e->state == PropertyNewValue )
   {
		switch (e->atom)
      {
         case XA_WM_NAME:
            if (client->store_name)
               XFree(client->store_name);
#ifdef I18N
            if (XGetWMName(display, client->window, &text_prop) != 0)
            {
               if (text_prop.encoding == XA_STRING)
               {
                  client->store_name = (char *)text_prop.value;
               }
               else
               {
                  XmbTextPropertyToTextList(display, &text_prop, &cl, &n);
                  if (cl)
                  {
                     client->store_name = strdup(cl[0]);
                     XFreeStringList(cl);
                  }
                  else
                  {
                     client->store_name = "NoName";
                  }
               }
            }
            else
            {
               client->store_name = "NoName";
            }
#else
            XFetchName(display, client->window, &client->store_name);
#endif

            /*
             * XXX: need a callback to let plugins know
             */
            if (client->state != IconicState)
               decor_titlechange(client);
            break;
         case XA_WM_NORMAL_HINTS:
            XGetWMNormalHints( display, client->window, &client->normalhints,
                               &supplied);
            break;
		}
   }
}

/* handler for EnterNotify */
static void event_enter_notify(XEvent *event)
{
	XCrossingEvent *e = &event->xcrossing;
	client_t *client;

	/*
	 * we should only get this event for sloppy/pointer focusing modes;
	 * when entering the frame window in those modes, we set the focus
	 * to that client.
	 */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
		return;
	if (e->window != client->frame)
		return;

	focus_setfocused(client);
}

/* handler for LeaveNotify */
static void event_leave_notify(XEvent *event)
{
	XCrossingEvent *e = &event->xcrossing;
	client_t *client;

	/*
	 * we should only recieve this event for pointer focusing
	 * mode; when leaving a window in pointer focus, the window
	 * is unfocused.
	 */
	if (XFindContext(display, e->window, client_context, (XPointer *) &client))
		return;
	if (e->window != client->frame)
		return;

	/*
	 * we check if the client has a workspace first, because we will get
	 * a leave notify if the user iconifies a window, and the window
	 * will have left it's workspace, and thus already be unfocused.
	 */
	if (client->workspace)
		focus_unfocus(client);
}

/* handler for ShapeNotify */
static void event_shape_notify(XEvent *event)
{
	client_t *client;

	/*
	 * when a client shapes it's window, we need to shape the frame
	 * window accordingly.
	 */
	if (XFindContext(display, event->xany.window, client_context, (XPointer *) &client))
		return;
	if (event->xany.window != client->window)
		return;
	client_shape(client);
}

/*
 * set up the table of event handlers; NULL out the events
 * that we don't handle.
 */
static void event_init()
{
	int i;

	for (i = 0; i < LASTEvent; i++)
   {
      handlers[i] = NULL;
   }

   // bva
   handlers[ConfigureNotify]  = event_configure_notify;
   // end bva

	handlers[DestroyNotify]		= event_destroy_notify;
	handlers[ConfigureRequest]	= event_configure_request;
	handlers[MapRequest]		   = event_map_request;
	handlers[MapNotify]		   = event_map_notify;
	handlers[UnmapNotify]		= event_unmap_notify;
	handlers[ButtonPress]		= event_button_press;
	handlers[ButtonRelease]		= event_button_release;
	handlers[KeyPress]		   = event_key_press;
	handlers[Expose]		      = event_expose;
	handlers[ClientMessage]		= event_client_message;
	handlers[PropertyNotify]	= event_property_notify;
	handlers[EnterNotify]		= event_enter_notify;
	handlers[LeaveNotify]		= event_leave_notify;
}


void hlog( XEvent *e )
{
   char type[200];

    switch( e->type )
    {
       case KeyPress:
          strcpy( type, "KeyPress" );
          break;
       case KeyRelease:
          strcpy( type, "KeyRelease" );
          break;
       case ButtonPress:
          strcpy( type, "ButtonPress" );
          break;
       case ButtonRelease:
          strcpy( type, "ButtonRelease" );
          break;
       case MotionNotify:
          strcpy( type, "MotionNotify" );
          break;
       case EnterNotify:
          strcpy( type, "EnterNotify" );
          break;
       case LeaveNotify:
          strcpy( type, "LeaveNotify" );
          break;
       case FocusIn:
          strcpy( type, "FocusIn" );
          break;
       case FocusOut:
          strcpy( type, "FocusOut" );
          break;
       case KeymapNotify:
          strcpy( type, "KeymapNotify" );
          break;
       case Expose:
          strcpy( type, "Expose" );
          break;
       case GraphicsExpose:
          strcpy( type, "GraphicsExpose" );
          break;
       case NoExpose:
          strcpy( type, "NoExpose" );
          break;
       case VisibilityNotify:
          strcpy( type, "VisibilityNotify" );
          break;
       case CreateNotify:
          strcpy( type, "CreateNotify" );
          break;
       case DestroyNotify:
          strcpy( type, "DestroyNotify" );
          break;
       case UnmapNotify:
          strcpy( type, "UnmapNotify" );
          break;
       case MapNotify:
          strcpy( type, "MapNotify" );
          break;
       case MapRequest:
          strcpy( type, "MapRequest" );
          break;
       case ReparentNotify:
          strcpy( type, "ReparentNotify" );
          break;
       case ConfigureNotify:
          strcpy( type, "ConfigureNotify" );
          break;
       case ConfigureRequest:
          strcpy( type, "ConfigureRequest" );
          break;
       case GravityNotify:
          strcpy( type, "GravityNotify" );
          break;
       case ResizeRequest:
          strcpy( type, "ResizeRequest" );
          break;
       case CirculateNotify:
          strcpy( type, "CirculateNotify" );
          break;
       case CirculateRequest:
          strcpy( type, "CirculateRequest" );
          break;
       case PropertyNotify:
          strcpy( type, "PropertyNotify" );
          break;
       case SelectionClear:
          strcpy( type, "SelectionClear" );
          break;
       case SelectionRequest:
          strcpy( type, "SelectionRequest" );
          break;
       case SelectionNotify:
          strcpy( type, "SelectionNotify" );
          break;
       case ColormapNotify:
          strcpy( type, "ColormapNotify" );
          break;
       case ClientMessage:
          strcpy( type, "ClientMessage" );
          break;
       case MappingNotify:
          strcpy( type, "MappingNotify" );
          break;

    }

    log( 2, "-> %s event for window 0x%x", type, (int) e->xany.window );

}

/*
 * handle an X event; this is exported to the world to allow pieces
 * of code that have their own internal event loops (for instance,
 * window movement) to call into the main handlers for certain
 * events (such as exposure events).
 */
void event_handle( XEvent *e )
{
   plugin_t *plugin;

   log( 0, "event_handle" );

   /*
    * check event for plugin association; these events
    * are dispatched to a plugin handler instead of getting
    * handled here.
    */
   if( XFindContext(display, e->xany.window, plugin_context, (XPointer *) &plugin) == 0 )
   {
      log( 2, "Plugin Context was found, let it handle the xevents, window %x\n",
           (int) e->xany.window );
      plugin_handle_event(plugin, e);

      return;
   }

   /*
    * if a handler exists in the table of event handlers,
    * dispatch the event to it.
    */
   if (e->type == shape_base + ShapeNotify)
   {
      event_shape_notify(e);
   }
   else if (handlers[e->type] != NULL)
   {
      hlog( e );
      handlers[e->type](e);
   }

}

/*
 * main loop for X event processing; when this routine
 * exits the window manager will die.
 */
void event_loop()
{
	XEvent event;

	event_init();

	while (1)
    {
       XNextEvent(display, &event);
       event_handle(&event);
       /*
        * if something sets restart_flag, we leave this function
        * and let main() take the appropriate action.
        */
       if ( restart_flag )
       {
          return;
       }
	}
}
