/*
 * src/events.c - event handling
 *
 * Copyright (C) 1999, 2000, 2001 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * This software 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this package; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.  
 */

#if HAVE_CONFIG_H
# include <config.h>
#endif 
/*  #define DEBUG */
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>

#include <libsizzle/libsizzle.h>

#include "alloc.h"
#include "screen.h"
#include "client.h"
#include "wimpwm.h"
#include "display.h"
#include "menus.h"
#include "workset.h"
#include "input.h"
#include "events.h"

#define STATE_IDLE          0
#define STATE_MOVE_MOUSE    1
#define STATE_MOVE_KEY      2
#define STATE_RESIZE_MOUSE  3
#define STATE_RESIZE_KEY    4
#define STATE_MENU_1        5
#define STATE_MENU_2        6
#define STATE_MENU_3        7
#define STATE_START_DELETE  8
#define STATE_CANCEL_DELETE 9

#define BUTTON_MASK \
  (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)

/*
 * Window manager state.  Used with STATE_* constants defined above.
 */
int current_state = STATE_IDLE;

/*
 * These are true when on of the prefix keys has been pressed.
 */
static int ctrl_x_prefix = 0;
static int ctrl_c_prefix = 0;

/*
 * Set this to non-zero to let the next WindowEnter event
 * go down the bytesink.
 */
int ignore_enter_event;

/*
 * Execute the given command COMMAND in a shell.  The command is automatically
 * put into the background.
 */
void
execute (char * command)
{
/*    int putenv(const char *string); */

  /*
   * Trick shamelessly stolen from wm2 - avoids zombies.
   */
  if (fork () == 0) 
    {
      if (fork () == 0) 
	{
	  /* 
	   * If you don't have putenv, miss out this next
	   * conditional and its contents.
	   */
	  if (display_string && (display_string[0] != '\0'))
	    {
	      char *pstring = (char *) xmalloc (strlen(display_string) + 10);
	      sprintf (pstring, "DISPLAY=%s", display_string);
	      putenv (pstring);
	    }
	  
	  execl ("/bin/sh", "/bin/sh", "-c", command, 0);
	  error ("exec sh -c '%s'", command);
	  exit (1);
	}
      exit (0);
    }
  wait ((int *) 0);
}

/*
 * Restart the window manager.  This routine only restarts when the
 * executable to be started can be found.
 */
void
restart (void)
{
  execlp (program_path, program_path, NULL);
  error ("Cannot exec %s", program_path);
}

/*
 * Dump a list of currently existing clients to STDERR.
 */
#if 0
static void
dump_clients (void)
{
  client_t client;

  err ("");
  err ("client list");
  for (client = client_root; client; client = client->next)
    {
      err("name: %s (state: %d, ignore_unmap: %d): window: %#x "
	  "(parent: %#x) @ x: %d,y: %d", client->name, get_wm_state(client),
	  client->ignore_unmap, client->window, client->parent, client->x, client->y);
    }
}
#endif /* 0 */

/*
 * Grab the keyboard and pointer for client CLIENT, so that we can
 * receive our hotkeys.
 */
static void
grab (client_t client)
{
  XUngrabKeyboard (display, CurrentTime);
  XGrabKey (display, AnyKey, HOTKEY_MOD, client->parent, True,
	    GrabModeAsync, GrabModeAsync);
  XGrabButton (display, AnyButton, HOTKEY_MOD, client->parent, True,
	       0, GrabModeAsync, GrabModeAsync, client->parent, None);
}

/*
 * Redraw old and new focus windows, install new colormap, put the keyboard
 * focus onto the new window and grab hotkeys to the new window.
 */
void
transfer_focus (client_t new, client_t old)
{
  if (old != NULL)
    redraw (old);
  redraw (new);

  XInstallColormap (display, new->cmap);
  XSetInputFocus (display, new->window, RevertToPointerRoot, CurrentTime);
  grab (new);
}

/*
 * Return the client next in the window list to CLIENT or NULL when
 * the client list is empty.
 */
static client_t
next_client (client_t client)
{
  if (client->next == NULL)
    return client_root;
  else
    return client->next;
}

/*
 * Return the previous client in the client list in respect to CLIENT.
 * Return NULL when the client list is empty.
 */
static client_t
prev_client (client_t client)
{
  client_t c;

  if (client == client_root)
    {
      c = client_root;
      while (c->next)
	c = c->next;
      return c;
    }
  else
    {
      c = client_root;
      while (c && c->next != client)
	c = c->next;
      return c;
    }
}

static int
cycle_candidate (client_t client)
{
  zzz_scm_t result;

  if (procedure_p (cycle_candidate_p))
    {
      result_t err;

      if ((err = zzz_apply (zzz_current_environment,
			     cycle_candidate_p,
			     zzz_make_n_list
			     (1, zzz_make_ro_string (client->name, -1)),
			     &result)) != RESULT_SUCCESS)
	return 1;
      if (zzz_eq (result, zzz_false_value))
	return 0;
      else
	return 1;
    }
  else
    return 1;
}

/*
 * Put the keyboard focus onto the next window in the client list, or
 * on the first if we reach the end of the list.  Only visible windows
 * are considered as candidates unless HIDDEN_TOO is non-zero.  Also
 * only windows in the current working set are used.  The new focused
 * window also gets raised.
 */
static void
cycle_focus (int hidden_too, int dir)
{
  client_t old_client;

  if (current_client == NULL)
    current_client = client_root;
  if (current_client == NULL)
    return;

  old_client = current_client;

  do
    {
      if (dir < 0)
	current_client = prev_client (current_client);
      else
	current_client = next_client (current_client);
    }
  while (current_client && old_client != current_client &&
	 ((current_client->wm_state == IconicState && !hidden_too) ||
	  (current_client->workset != current_workset &&
	   current_client->workset != -1) ||
	  !cycle_candidate (current_client)));

  if (current_client != NULL && current_client != old_client)
    {
      if (current_client->wm_state == IconicState)
	{
	  unhide_client (current_client);
	}
      else
	client_raise (current_client);
      transfer_focus (current_client, old_client);
    }
}

#if 1
void
focus_any_client (void)
{
  client_t client, old_current;

  client = NULL;
  for (client = client_root;
       client && (client->workset != current_workset ||
		  client->wm_state == IconicState);
       client = client->next)
    continue;
  if (client)
    {
/*        error ("focusing\n"); */
      old_current = current_client;
      current_client = client;
      transfer_focus (current_client, old_current);
      client_raise (current_client);
    }
}
#endif

/* Returns 1 if the point is in the box. */
static int
point_in_box (client_t client, int x, int y)
{
  return ((x >= client->tab_width - client->title_height)
	  && (x <= client->tab_width)
	  && (y <= client->title_height)
	  && (y >= 0));
}

/*
 * Handle the mouse button press event E.
 */
static void
handle_button_press_event (XButtonEvent * e)
{
  if (e->window == root_window)
    {
      if ((e->state & BUTTON_MASK) == 0)
	{
	  switch (e->button)
	    {
	    case Button1:
	      create_menu_window (BUTTON_1_MENU);
	      break;
	    case Button2:
	      create_menu_window (BUTTON_2_MENU);
	      break;
	    case Button3:
	      create_menu_window (BUTTON_3_MENU);
	      break;
	    }
	}
    }
  else
    {
      client_t client = find_client_from_tab (e->window);
      if (client)
	{
	  int box = point_in_box (client, e->x, e->y);
	  switch (e->button)
	    {
	    case Button1:
	      if (box)
		hide_client (client);
	      else
		client_raise (client);
	      break;
	    case Button2:
	      if (box)
		resize_client (client);
	      else
		move_client (client);
	      break;
	    case Button3:
	      if (box)
		{
		  XDefineCursor (display, client->tab,
				 get_font_cursor (XC_pirate));
		  current_state = STATE_START_DELETE;
		}
	      else
		client_lower (client);
	      break;
	    }
	}
      else
	{
	  client = find_client (e->window, 1);
	  if (client)
	    {
	      switch (e->button)
		{
		case Button1:
		  client_raise (client);
		  break;
		case Button2:
		  move_client (client);
		  break;
		case Button3:
		  client_lower (client);
		  break;
		}
	    }
	}
    }
}

/*
 * Handle the mouse button release event E.
 */
static void
handle_button_release_event (XButtonEvent * e)
{
  if (current_state == STATE_START_DELETE
      || current_state == STATE_CANCEL_DELETE)
    {
      client_t client = find_client_from_tab (e->window);
      if (client)
	{
	  if (current_state == STATE_START_DELETE)
	    send_wm_delete (client);
	  else
	    XDefineCursor (display, client->tab,
			   get_font_cursor (XC_top_left_arrow));
	}
      current_state = STATE_IDLE;
    }
  else if (e->window == root_window)
    {
      switch (e->button)
	{
	case Button1:
	  if (menus[BUTTON_1_MENU].window != None &&
	      e->subwindow == menus[BUTTON_1_MENU].window)
	    menu_select (e, BUTTON_1_MENU);
	  destroy_menu_window (BUTTON_1_MENU);
	  break;
	case Button2:
	  if (menus[BUTTON_2_MENU].window != None &&
	      e->subwindow == menus[BUTTON_2_MENU].window)
	    menu_select (e, BUTTON_2_MENU);
	  destroy_menu_window (BUTTON_2_MENU);
	  break;
	case Button3:
	  if (menus[BUTTON_3_MENU].window != None &&
	      e->subwindow == menus[BUTTON_3_MENU].window)
	    menu_select (e, BUTTON_3_MENU);
	  destroy_menu_window (BUTTON_3_MENU);
	  break;
	}
    }
}

/*
 * Handle the mouse motion event E.
 */
static void
handle_motion_event (XMotionEvent * e)
{
  if (e->window == root_window)
    {
      int i;

      for (i = BUTTON_1_MENU; i < BUTTON_5_MENU + 1; i++)
	{
	  if (menus[i].window != None)
	    {
	      if (e->subwindow == menus[i].window)
		menu_highlight (e, i);
	      else
		menu_highlight (NULL, i);
	    }
	}
    }
  else if (current_state == STATE_START_DELETE)
    {
      client_t client = find_client_from_tab (e->window);
      if (client)
	{
	  if (!point_in_box (client, e->x, e->y))
	    {
	      XDefineCursor (display, client->tab,
			     get_font_cursor (XC_rightbutton));
	      current_state = STATE_CANCEL_DELETE;
	    }
	}
      else
	error ("confused delete state");
    }
}

/*
 * Handle the configuration event E.
 */
static void
handle_configure_request (XConfigureRequestEvent * e)
{
  client_t client;
  XWindowChanges wc;

  if ((client = find_client (e->window, 0)))
    {
      if (e->value_mask & CWX)
	client->x = e->x - border_width;
      if (e->value_mask & CWY)
	client->y = e->y - border_width;
      if (e->value_mask & CWWidth)
	client->width = e->width;
      if (e->value_mask & CWHeight)
	client->height = e->height;

      if (client->x < 0)
	client->x = 0;
      if (client->y < 0)
	client->y = client->title_height;

      wc.x = client->x;
      wc.y = client->y;
      wc.width = client->width;
      wc.height = client->height;
      wc.border_width = border_width;
      wc.sibling = e->above;
      wc.stack_mode = e->detail;
      XConfigureWindow (display, client->parent, e->value_mask, &wc);
      send_config (client);
    }
  wc.x = client ? 0 : e->x;
  wc.y = client ? 0 : e->y;
  wc.width = e->width;
  wc.height = e->height;
  wc.sibling = e->above;
  wc.stack_mode = e->detail;
  XConfigureWindow (display, e->window, e->value_mask, &wc);
}

/*
 * Handle the window mapping event E.
 */
static void
handle_map_request (XMapRequestEvent * e)
{
  client_t client;

  if ((client = find_client (e->window, 0)))
    {
      XMapWindow (display, client->window);
      XMapRaised (display, client->parent);
      set_wm_state (client, NormalState);
      client->wm_state = NormalState;
    }
  else
    create_client_window (e->window, NULL);
}

/*
 * Handle the window unmapping event E.
 */
static void
handle_unmap_event (XUnmapEvent * e)
{
  client_t client;

  if ((client = find_client (e->window, 0)))
    {
      if (client->ignore_unmap > 0)
	client->ignore_unmap--;
      else
	{
	  remove_client (client);
	}
    }
}

/*
 * Handle the client generated message E.
 */
static void
handle_client_message (XClientMessageEvent * e)
{
  client_t client;

  if ((client = find_client (e->window, 0))
      && e->message_type == _XA_WM_CHANGE_STATE &&
      e->format == 32 && e->data.l[0] == IconicState)
    hide_client (client);
}

/*
 * Handle the colormap notification event E.
 */
static void
handle_colormap_change (XColormapEvent * e)
{
  client_t client;

  if ((client = find_client (e->window, 0))
      && e->new)
    {
      client->cmap = e->colormap;
      XInstallColormap (display, client->cmap);
    }
}

/*
 * Handle the property notification event E.
 */
static void
handle_property_change (XPropertyEvent * e)
{
  client_t client;
  long dummy;

  if ((client = find_client (e->window, 0)))
    {
      switch (e->atom)
	{
	case XA_WM_NAME:
	  if (client->name)
	    XFree(client->name);
	  XFetchName (display, client->window, &client->name);
	  client_adjust_tab_width (client);
	  XClearWindow (display, client->tab);
	  redraw(client);
	  break;

	case XA_WM_NORMAL_HINTS:
	  XGetWMNormalHints (display, client->window, client->size, &dummy);
	  break;
	}
    }
}

static void
grab_keys_for_input (void)
{
  client_t c = current_client;

  current_client = NULL;
  if (c)
    redraw (c);

  XSetInputFocus (display, input_window, RevertToPointerRoot, CurrentTime);
  XUngrabKeyboard(display, CurrentTime);
  XGrabKey(display, AnyKey, AnyModifier, input_window, True, GrabModeAsync,
	   GrabModeAsync);
}

/*
 * Handle the mouse pointer enter event E.
 */
static void
handle_enter_event (XCrossingEvent * e)
{
  client_t client;
  client_t old_current;

  if (current_state == STATE_RESIZE_KEY ||
      current_state == STATE_MOVE_KEY)
    return;

  if (ignore_enter_event)
    {
      ignore_enter_event = 0;
      return;
    }
  if ((client = find_client (e->window, 0)))
    {
      old_current = current_client;
      current_client = client;
      transfer_focus (current_client, old_current);
    }
  else if ((client = find_client_from_tab (e->window)))
    {
      old_current = current_client;
      current_client = client;
      transfer_focus (current_client, old_current);
    }
  else if (input_window != None && e->window == input_window)
    {
/*        XBell (display, 0); */
      grab_keys_for_input ();
    }
}

/*
 * Handle the expose event E.
 */
static void
handle_expose_event (XExposeEvent * e)
{
  client_t client;
  int i;

  if (e != NULL)
    {
      if (workset_window != None && e->window == workset_window)
	{
	  draw_workset_window ();
	  return;
	}
      if (input_window != None && e->window == input_window)
	{
	  draw_input_window ();
	  return;
	}
      for (i = BUTTON_1_MENU; i < BUTTON_5_MENU + 1; i++)
	{
	  if (menus[i].window != None && e->window == menus[i].window)
	    {
	      menu_draw (-1, i);
	      return;
	    }
	}
      if (e->count == 0)
	{
	  /*
	   * Handle only the last of multiple expose events.
	   */
	  if ((client = find_client_from_tab (e->window)))
	    redraw (client);
	}
    }
}

/*
 * FIXME: Find out what this one is supposed to do.
 */
static void
handle_reparent_event (XReparentEvent * e)
{
  e = e;
  /*	client_tc = find_client(e->window, 0);
	client_tdest = find_client(e->parent, 1);

	if (client && !dest) remove_client(client, 0);*/
}

/*
 * FIXME: Find out what this one is supposed to do.
 */
#ifdef SHAPE
static void
handle_shape_change (XShapeEvent * e)
{
  client_t client;

  if ((client = find_client (e->window, 0)))
    set_shape (client);
}
#endif

/*
 * Handle the key press event E.
 */
static void
handle_key_event (XKeyEvent *e)
{
  client_t client;

  client = find_client(e->window, 1);
  if (!client)
    client = find_client(e->window, 0);

  if (input_window != None && e->window == input_window)
    {
      input_handle_key (e);
      return;
    }
  if (ctrl_c_prefix)
    {
      switch (XKeycodeToKeysym(display, e->keycode, 0))
	{
	  /*
	   * Abort key, beeps and releases prefix keys.
	   */
	case XK_g:
	  XBell (display, 0);
	  break;

	  /*
	   * Hotkeys for several applications.
	   */
	case XK_e:
	  execute ("emacs");
	  break;
	case XK_n:
	  execute ("netscape");
	  break;
	case XK_r:
	  execute ("xterm -title 'Root Shell' -bg rgb:ff/60/60 -e su -");
	  break;
	case XK_u:
	  execute ("xterm -title Uni -e ssh -l mgrabmue fiesta.cs.tu-berlin.de");
	  break;
	case XK_x:
	  execute ("xterm");
	  break;

	default:
	  /* FIXME: This is the place where we will check for defined 
	   * commands in the future.
	   */
	  break;
	}
      ctrl_x_prefix = 0;
      ctrl_c_prefix = 0;

      current_state = STATE_IDLE;
    }
  else if (ctrl_x_prefix)
    {
      switch (XKeycodeToKeysym(display, e->keycode, 0))
	{
	  /*
	   * Abort key, beeps and releases prefix keys.
	   */
	case XK_g:
	  XBell (display, 0);
	  break;

	  /* Full-screenify the current window.  */
	case XK_f:
	  full_screen_client (current_client);
	  break;

	  /*
	   * Quit the window manager.
	   */
	case XK_c:
	  exit (0);
	  break;

	case XK_r:
	  read_user_init_file ();
/*  	  display_reset_variables (); */
	  break;

	case XK_s:
	  client = manually_select_window ();
	  if (client)
	    {
	      if (client->workset == -1)
		client->workset = current_workset;
	      else
		client->workset = -1;
	    }
	  break;

	case XK_i:
	  input_create ();
	  grab_keys_for_input ();
	  break;

	  /*
	   * Restart the window manager, if possible.
	   */
	case XK_Escape:
	  restart ();
	  break;

	  /*
	   * Hide the current window.
	   */
	case XK_0:
	  if (client != NULL)
	    hide_client (client);
	  break;

	  /*
	   * Hide all windows but the current.
	   */
	case XK_1:
	  if (client != NULL)
	    {
	      client_t c;
	      for (c = client_root; c; c = c->next)
		if (c != client)
		  hide_client (c);
	    }
	  break;

	  /*
	   * Make all windows visible.
	   */
	case XK_b:
	  for (client = client_root; client; client = client->next)
	    {
	      if (get_wm_state(client) == IconicState)
		{
		  unhide_client (client);
		}
	    }
	  break;

	}
      ctrl_x_prefix = 0;
      ctrl_c_prefix = 0;

      current_state = STATE_IDLE;
    }
  else
    {
      switch (XKeycodeToKeysym(display, e->keycode, 0))
	{
	  /*
	   * Abort key, beeps and releases prefix keys.
	   */
	case XK_g:
	  current_state = STATE_IDLE;
	  XBell (display, 0);
	  break;

	  /*
	   * C-A-x is a prefix key.
	   */
	case XK_x:
	  ctrl_x_prefix = 1;
	  ctrl_c_prefix = 0;
	  current_state = STATE_IDLE;
	  break;


	  /*
	   * C-A-c is a prefix key.
	   */
	case XK_c:
	  ctrl_c_prefix = 1;
	  ctrl_x_prefix = 0;
	  current_state = STATE_IDLE;
	  break;

	  /*
	   * The following keys position the current window relatively.
	   */
	case XK_Up:
	case XK_KP_Up:
	case XK_p:
	  switch (current_state)
	    {
	    case STATE_IDLE:
	      cycle_focus (0, -1);
	      break;
	    case STATE_MOVE_KEY:
	      if (client != NULL)
		{
/*  		  suppress_enter = 1; */
		  move_client_to (client, 1, 0, -16);
		  transfer_focus (client, NULL);
		}
	      break;
	    case STATE_RESIZE_KEY:
	      if (client != NULL)
		{
		  resize_client_to (client, 0, -1);
		}
	      break;
	    }
	  break;
	case XK_Down:
	case XK_KP_Down:
	case XK_n:
	  switch (current_state)
	    {
	    case STATE_IDLE:
	      cycle_focus (0, 1);
	      break;
	    case STATE_MOVE_KEY:
	      if (client != NULL)
		{
/*  		  suppress_enter = 1; */
		  move_client_to(client, 1, 0, 16);
		  transfer_focus (client, NULL);
		}
	      break;
	    case STATE_RESIZE_KEY:
	      if (client != NULL)
		{
		  resize_client_to (client, 0, 1);
		}
	      break;
	    }
	  break;
	case XK_Left:
	case XK_KP_Left:
	case XK_b:
	  switch (current_state)
	    {
	    case STATE_IDLE:
	      workset_next (-1);
	      break;
	    case STATE_MOVE_KEY:
	      if (client != NULL)
		{
/*  		  suppress_enter = 1; */
		  move_client_to(client, 1, -16, 0);
		  transfer_focus (client, NULL);
		}
	      break;
	    case STATE_RESIZE_KEY:
	      if (client != NULL)
		{
		  resize_client_to (client, -1, 0);
		}
	      break;
	    }
	  break;
	case XK_Right:
	case XK_KP_Right:
	case XK_f:
	  switch (current_state)
	    {
	    case STATE_IDLE:
	      workset_next (1);
	      break;
	    case STATE_MOVE_KEY:
	      if (client != NULL)
		{
/*  		  suppress_enter = 1; */
		  move_client_to(client, 1, 16, 0);
		  transfer_focus (client, NULL);
		}
	      break;
	    case STATE_RESIZE_KEY:
	      if (client != NULL)
		{
		  resize_client_to (client, 1, 0);
		}
	      break;
	    }
	  break;

	  /*
	   * The following keys position the current window absolutely.
	   */
	case XK_Home:
	case XK_KP_Home:
	  current_state = STATE_IDLE;
	  if (client != NULL)
	    {
/*  	      suppress_enter = 1; */
	      move_client_to(client, 0,  0, client->title_height);
	      transfer_focus (client, NULL);
	    }
	  break;
	case XK_End:
	case XK_KP_End:
	  current_state = STATE_IDLE;
	  if (client != NULL)
	    {
/*  	      suppress_enter = 1; */
	      move_client_to(client, 0, 0,
		      DisplayHeight(display, default_screen)
			     - client->height - 2 * border_width);
	      transfer_focus (client, NULL);
	    }
	  break;
	case XK_Page_Up:
	case XK_KP_Page_Up:
	  current_state = STATE_IDLE;
	  if (client != NULL)
	    {
/*  	      suppress_enter = 1; */
	      move_client_to(client, 0,
		      DisplayWidth(display, default_screen) - client->width
			     - 2 * border_width,
		      client->title_height);
	      transfer_focus (client, NULL);
	    }
	  break;
	case XK_Page_Down:
	case XK_KP_Page_Down:
	  current_state = STATE_IDLE;
	  if (client != NULL)
	    {
/*  	      suppress_enter = 1; */
	      move_client_to (client, 0,
		      DisplayWidth(display, default_screen)
			      - client->width - 2 * border_width,
		      DisplayHeight(display, default_screen)
			      - client->height - 2 * border_width);
	      transfer_focus (client, NULL);
	    }
	  break;

	  /*
	   * C-A-Ret and C-A-Space cycle windows, C-A-Space includes
	   * hidden windows.
	   */
	case XK_Return:
/*  	  cycle_focus(0, 1); */
	  current_state = STATE_IDLE;
	  break;

	case XK_m:
	  current_state = STATE_MOVE_KEY;
	  break;

	case XK_r:
	  current_state = STATE_RESIZE_KEY;
	  break;

/*  	case XK_space: */
/*  	  cycle_focus(1, 1); */
/*  	  break; */

	case XK_l:			/* Debugging support. */
/*  	  fprintf(stderr, "Allocated bytes: %d\n", allocated_bytes); */
/*  	  dump_clients (); */
	  break;

#if 0
	case XK_m:
	  client = manually_select_window ();
	  if (client)
	    XRaiseWindow (display, client->parent);
	  break;
#endif /* 0 */

	}
    }
}

#ifdef DEBUG
void
show_event (XEvent e)
{
  char *s;
  Window w, c = None;

  switch (e.type)
    {
    case ButtonPress:
      s = "ButtonPress"; 
      w = e.xbutton.window;
      break;
    case ButtonRelease:
      s = "ButtonRelease"; 
      w = e.xbutton.window;
      break;
    case ClientMessage:
      s = "ClientMessage"; 
      w = e.xclient.window;
      break;
    case ColormapNotify:
      s = "ColormapNotify"; 
      w = e.xcolormap.window;
      break;
    case ConfigureNotify:
      s = "ConfigNotify"; 
      w = e.xconfigure.window;
      break;
    case ConfigureRequest:
      s = "ConfigRequest"; 
      w = e.xconfigurerequest.window;
      break;
    case CreateNotify:
      s = "CreateNotify"; 
      w = e.xcreatewindow.window;
      break;
    case DestroyNotify:
      s = "DestroyNotify"; 
      w = e.xdestroywindow.window;
      break;
    case EnterNotify:
      s = "EnterNotify";
      w = e.xcrossing.window;
      break;
    case Expose:
      s = "ExposeEvent";
      w = e.xexpose.window;
      break;
    case MapNotify:
      s = "MapNotify";
      w = e.xmap.window;
      break;
    case MapRequest:
      s = "MapRequest";
      w = e.xmaprequest.window;
      break;
    case MappingNotify:
      s = "MappingNotify";
      w = e.xmapping.window;
      break;
    case MotionNotify:
      s = "MotionNotify";
      w = e.xmotion.window;
      c = e.xmotion.subwindow;
      break;
    case PropertyNotify:
      s = "PropertyNotify";
      w = e.xproperty.window;
      break;
    case ReparentNotify:
      s = "ReparentNotify";
      w = e.xreparent.window;
      break;
    case ResizeRequest:
      s = "ResizeRequest";
      w = e.xresizerequest.window;
      break;
    case UnmapNotify:
      s = "UnmapNotify";
      w = e.xunmap.window;
      break;
    default:
      s = "unknown event";
      w = None;
      break;
    }
  
  err("recieved %s,\twindow = %#x,\tchild = %#x", s, w, c);
}
#endif

/*
 * Query the X server for events and process them in a loop.
 */
void
process_events (void)
{
  XEvent event;
  int err;

  while (1)
    {
      if (!XCheckMaskEvent(display, ChildMask | ColormapChangeMask
			   | PropertyChangeMask | KeyPressMask
			   | MouseMask | ExposureMask
			   | EnterWindowMask, &event))
	{
	  do
	    {
	      struct timeval timeout;
	      fd_set rd_set;
	  
	      /*
	       * Flush X output queue to avoid redrawing delays.
	       */
	      XFlush (display);
	  
	      FD_ZERO(&rd_set);
	      FD_SET(ConnectionNumber(display), &rd_set);
	      timeout.tv_sec = 0;
	      timeout.tv_usec = 50000;
	      err = select(ConnectionNumber(display) + 1, &rd_set, 
			   NULL, NULL, &timeout);
	      if (err == 0)
		{
		  workset_tick ();
		  client_tick ();
		  input_tick ();
		}
	    }
	  while (err == 0);

	  XNextEvent(display, &event);
	}

#ifdef DEBUG 
      show_event (event);
#endif
      switch (event.type)
	{
	case KeyPress:
	  handle_key_event (&event.xkey);
	  break;
	case KeyRelease:
	  break;
	case ButtonPress:
	  handle_button_press_event (&event.xbutton);
	  break;
	case ButtonRelease:
	  handle_button_release_event (&event.xbutton);
	  break;
	case MotionNotify:
	  handle_motion_event (&event.xmotion);
	  break;
	case ConfigureRequest:
	  handle_configure_request (&event.xconfigurerequest);
	  break;
	case MapRequest:
	  handle_map_request (&event.xmaprequest);
	  break;
	case UnmapNotify:
	  handle_unmap_event (&event.xunmap);
	  break;
	case ClientMessage:
	  handle_client_message (&event.xclient);
	  break;
	case ColormapNotify:
	  handle_colormap_change (&event.xcolormap);
	  break;
	case PropertyNotify:
	  handle_property_change (&event.xproperty);
	  break;
	case EnterNotify:
	  handle_enter_event (&event.xcrossing);
	  break;
	case Expose:
	  handle_expose_event (&event.xexpose);
	  break;
	case ReparentNotify:
	  handle_reparent_event (&event.xreparent);
	  break;
#ifdef SHAPE
	default:
	  if (shape && event.type == shape_event)
	    handle_shape_change ((XShapeEvent *)&event);
#endif
	}
    }
}

