/* Events: printing them, converting them to and from characters.
   Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc.

This file is part of XEmacs.

XEmacs 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.

XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Synched up with: Not in FSF. */

/* This file has been Mule-ized. */

#include <config.h>
#include "lisp.h"
#include "buffer.h"
#include "window.h"
#include "device.h"
#include "device-tty.h" /* for stuff in character_to_event */
#include "device-x.h"	/* for x_event_name prototype */
#include "frame.h"
#include "events.h"
#include "keymap.h"
#include "extents.h"	/* Just for the EXTENTP abort check... */
#include "redisplay.h"

/* Where old events go when they are explicitly deallocated.
   The event chain here is cut loose before GC, so these will be freed
   eventually.
 */
static struct Lisp_Event *event_resource;

Lisp_Object Qeventp;
Lisp_Object Qevent_live_p;
Lisp_Object Qkey_press_event_p;
Lisp_Object Qbutton_event_p;
Lisp_Object Qmouse_event_p;
Lisp_Object Qprocess_event_p;

/* #### Ad-hoc hack.  Should be part of define_lrecord_implementation */
void
clear_event_resource (void)
{
  event_resource = 0;
}

static Lisp_Object mark_event (Lisp_Object, void (*) (Lisp_Object));
static void print_event (Lisp_Object, Lisp_Object, int);
static int event_equal (Lisp_Object, Lisp_Object, int);
static unsigned long event_hash (Lisp_Object obj, int depth);
DEFINE_LRECORD_IMPLEMENTATION ("event", event,
                               mark_event, print_event, 0, event_equal,
			       event_hash, struct Lisp_Event);

/* Make sure we lose quickly if we try to use this event */
static void
deinitialize_event (struct Lisp_Event *event)
{
  int i;

  for (i = 0; i < ((sizeof (struct Lisp_Event)) / sizeof (int)); i++)
    ((int *) event) [i] = 0xdeadbeef;
  event->event_type = dead_event;
  event->device = Qnil;
  set_lheader_implementation (&(event->lheader), lrecord_event);
  event_next (event) = 0;
}

static Lisp_Object
mark_event (Lisp_Object obj, void (*markobj) (Lisp_Object))
{
  struct Lisp_Event *event = XEVENT (obj);

  switch (event->event_type)
    {
    case key_press_event:
      ((markobj) (event->event.key.keysym));
      break;
    case process_event:
      ((markobj) (event->event.process.process));
      break;
    case timeout_event:
      ((markobj) (event->event.timeout.function));
      ((markobj) (event->event.timeout.object));
      break;
    case eval_event:
    case misc_user_event:
      ((markobj) (event->event.eval.function));
      ((markobj) (event->event.eval.object));
      break;
    case button_press_event:
    case button_release_event:
    case pointer_motion_event:
    case magic_event:
    case empty_event:
    case dead_event:
      break;
    default:
      abort ();
    }
  ((markobj) (event->channel));
  ((markobj) (event->device));
  event = event_next (event);
  if (!event)
    return (Qnil);
  XSETEVENT (obj, event);
  return (obj);
}

static void
print_event_1 (CONST char *str, Lisp_Object obj, Lisp_Object printcharfun)
{
  char buf[255];
  write_c_string (str, printcharfun);
  format_event_object (buf, XEVENT (obj), 0);
  write_c_string (buf, printcharfun);
}

static void
print_event (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
{
  if (print_readably)
    error ("printing unreadable object #<event>");

  switch (XEVENT (obj)->event_type) 
    {
    case key_press_event:
      print_event_1 ("#<keypress-event ", obj, printcharfun);
      break;
    case button_press_event:
      print_event_1 ("#<buttondown-event ", obj, printcharfun);
      break;
    case button_release_event:
      print_event_1 ("#<buttonup-event ", obj, printcharfun);
      break;
    case magic_event:
      print_event_1 ("#<magic-event ", obj, printcharfun);
      break;
    case pointer_motion_event:
      {
	char buf[100];
	sprintf (buf, "#<motion-event %d, %d",
		 XEVENT (obj)->event.motion.x, XEVENT (obj)->event.motion.y);
	write_c_string (buf, printcharfun);
	break;
      }
    case process_event:
      {
	write_c_string ("#<process-event ", printcharfun);
	print_internal (XEVENT (obj)->event.process.process, printcharfun, 1);
	break;
      }
    case timeout_event:
      {
	write_c_string ("#<timeout-event ", printcharfun);
	print_internal (XEVENT (obj)->event.timeout.object, printcharfun, 1);
	break;
      }
    case empty_event:
      {
	write_c_string ("#<empty-event", printcharfun);
	break;
      }
    case misc_user_event:
    case eval_event:
      {
	write_c_string ("#<", printcharfun);
	if (XEVENT (obj)->event_type == misc_user_event)
	  write_c_string ("misc-user", printcharfun);
	else
	  write_c_string ("eval", printcharfun);
	write_c_string ("-event (", printcharfun);
	print_internal (XEVENT (obj)->event.eval.function, printcharfun, 1);
	write_c_string (" ", printcharfun);
	print_internal (XEVENT (obj)->event.eval.object, printcharfun, 1);
	write_c_string (")", printcharfun);
	break;
      }
    case dead_event:
      {
	write_c_string ("#<DEALLOCATED-EVENT", printcharfun);
	break;
      }
    default:
      {
	write_c_string ("#<UNKNOWN-EVENT-TYPE", printcharfun);
	break;
      }
    }
  write_c_string (">", printcharfun);
}
  
static int
event_equal (Lisp_Object o1, Lisp_Object o2, int depth)
{
  struct Lisp_Event *e1 = XEVENT (o1);
  struct Lisp_Event *e2 = XEVENT (o2);

  if (e1->event_type != e2->event_type) return 0;
  if (!EQ (e1->channel, e2->channel)) return 0;
  if (!EQ (e1->device, e2->device)) return 0;
/*  if (e1->timestamp != e2->timestamp) return 0; */
  switch (e1->event_type)
    {
    case process_event:
      return (EQ (e1->event.process.process,
		  e2->event.process.process));
    
    case timeout_event:
      if (NILP (Fequal (e1->event.timeout.function,
			e2->event.timeout.function)))
	return 0;
      if (NILP (Fequal (e1->event.timeout.object,
			e2->event.timeout.object)))
	return 0;
      return 1;
    
    case key_press_event:
      return ((EQ (e1->event.key.keysym,
                   e2->event.key.keysym)
               && (e1->event.key.modifiers
                   == e2->event.key.modifiers)));

    case button_press_event:
    case button_release_event:
      return (((e1->event.button.button
                == e2->event.button.button)
               && (e1->event.button.modifiers
                   == e2->event.button.modifiers)));

    case pointer_motion_event:
      return ((e1->event.motion.x == e2->event.motion.x
               && e1->event.motion.y == e2->event.motion.y));

    case misc_user_event:
    case eval_event:
      if (NILP (Fequal (e1->event.eval.function,
			e2->event.eval.function)))
	return 0;
      if (NILP (Fequal (e1->event.eval.object,
			e2->event.eval.object)))
	return 0;
      return 1;
    case magic_event:
      if (!EQ (e1->device, e2->device))
	return 0;

#ifdef HAVE_X_WINDOWS
      /* XEvent is actually a union which means that we can't just use == */
      if (DEVICE_IS_X (XDEVICE (e1->device)))
	return (!memcmp ((XEvent *) &e1->event.magic.underlying_x_event,
			 (XEvent *) &e2->event.magic.underlying_x_event,
			 sizeof (e1->event.magic.underlying_x_event)));
#endif
#ifdef HAVE_NEXTSTEP
      if (DEVICE_IS_NS (XDEVICE (e1->device)))
	return (e1->event.magic.underlying_ns_event ==
		e2->event.magic.underlying_ns_event);
#endif
      return (e1->event.magic.underlying_tty_event ==
	      e2->event.magic.underlying_tty_event);

    case empty_event:      /* Empty and deallocated events are equal. */
    case dead_event:
      return 1;

    default:
      abort ();
      return 0;                 /* not reached; warning suppression */
    }
}

static unsigned long
event_hash (Lisp_Object obj, int depth)
{
  struct Lisp_Event *e = XEVENT (obj);
  unsigned long hash;

  hash = HASH3 (e->event_type, LISP_HASH (e->channel), LISP_HASH (e->device));
  switch (e->event_type)
    {
    case process_event:
      return HASH2 (hash, LISP_HASH (e->event.process.process));
    
    case timeout_event:
      return HASH3 (hash, internal_hash (e->event.timeout.function, depth + 1),
		    internal_hash (e->event.timeout.object, depth + 1));
    
    case key_press_event:
      return HASH3 (hash, LISP_HASH (e->event.key.keysym),
		    e->event.key.modifiers);

    case button_press_event:
    case button_release_event:
      return HASH3 (hash, e->event.button.button, e->event.button.modifiers);

    case pointer_motion_event:
      return HASH3 (hash, e->event.motion.x, e->event.motion.y);

    case misc_user_event:
    case eval_event:
      return HASH3 (hash, internal_hash (e->event.eval.function, depth + 1),
		    internal_hash (e->event.eval.object, depth + 1));

    case magic_event:
#ifdef HAVE_X_WINDOWS
      if (DEVICE_IS_X (XDEVICE (e->device)))
	return
	  HASH2 (hash,
		 memory_hash (&e->event.magic.underlying_x_event,
			      sizeof (e->event.magic.underlying_x_event)));
#endif
#ifdef HAVE_NEXTSTEP
        if (DEVICE_IS_NS (XDEVICE (e->device)))
           return
	     HASH2 (hash,
		    memory_hash (&e->event.magic.underlying_ns_event,
				 sizeof (e->event.magic.underlying_ns_event)));
#endif
	return
	  HASH2 (hash,
		 memory_hash (&e->event.magic.underlying_tty_event,
			      sizeof (e->event.magic.underlying_tty_event)));

    case empty_event:
    case dead_event:
      return hash;

    default:
      abort ();
    }

  return 0;
}


DEFUN ("allocate-event", Fallocate_event, Sallocate_event, 0, 0, 0,
  "Return an empty event structure.\n\
WARNING, the event object returned may be a reused one; see the function\n\
`deallocate-event'.")
    ()
{
  struct Lisp_Event *e;
  Lisp_Object event;
  if (event_resource)
    {
      e = event_resource;
      event_resource = event_next (e);
      XSETEVENT (event, e);
    }
  else
    {
      event = make_event ();
      e = XEVENT (event);
    }
  deinitialize_event (e);
  e->event_type = empty_event;
  set_event_next (e, 0);
  e->timestamp = 0;
  e->channel = Qnil;
  e->device = Qnil;
  return event;
}

DEFUN ("deallocate-event", Fdeallocate_event, Sdeallocate_event, 1, 1, 0,
  "Allow the given event structure to be reused.\n\
You MUST NOT use this event object after calling this function with it.\n\
You will lose.  It is not necessary to call this function, as event\n\
objects are garbage-collected like all other objects; however, it may\n\
be more efficient to explicitly deallocate events when you are sure\n\
that that is safe.")
    (event)
    Lisp_Object event;
{
  struct Lisp_Event *e;
  CHECK_EVENT (event, 0);

  e = XEVENT (event);
  if (e->event_type == dead_event)
    error ("this event is already deallocated!");

  if (e->event_type > last_event_type)
    abort ();

#if 0
  {  
    int i;
    extern Lisp_Object Vlast_command_event;
    extern Lisp_Object Vlast_input_event, Vunread_command_event;
    extern Lisp_Object Vthis_command_keys, Vrecent_keys_ring;

    if (EQ (event, Vlast_command_event))
      abort ();
    if (EQ (event, Vlast_input_event))
      abort ();
    if (EQ (event, Vunread_command_event))
      abort ();
    for (i = 0; i < XVECTOR (Vthis_command_keys)->size; i++)
      if (EQ (event, vector_data (XVECTOR (Vthis_command_keys)) [i]))
	abort ();
    for (i = 0; i < XVECTOR (Vrecent_keys_ring)->size; i++)
      if (EQ (event, vector_data (XVECTOR (Vrecent_keys_ring)) [i]))
	abort ();
  }
#endif /* 0 */

  if (e == event_resource)
    abort ();
  deinitialize_event (e);
#ifndef ALLOC_NO_POOLS
  set_event_next (e, event_resource);
  event_resource = e;
#endif
  return Qnil;
}

DEFUN ("copy-event", Fcopy_event, Scopy_event, 1, 2, 0,
  "Make a copy of the given event object.\n\
If a second argument is given, the first event is copied into the second\n\
and the second is returned.  If the second argument is not supplied (or\n\
is nil) then a new event will be made as with `allocate-event.'  See also\n\
the function `deallocate-event'.")
     (event1, event2)
     Lisp_Object event1, event2;
{
  struct Lisp_Event *e1, *e2;

  CHECK_LIVE_EVENT (event1, 0);
  if (NILP (event2))
    event2 = Fallocate_event ();
  else CHECK_LIVE_EVENT (event2, 0);
  if (EQ (event1, event2))
    return signal_simple_continuable_error_2
      ("copy-event called with `eq' events", event1, event2);
  e1 = XEVENT (event1);
  e2 = XEVENT (event2);

  if (e1->event_type > last_event_type ||
      e2->event_type > last_event_type)
    abort ();
  {
    struct Lisp_Event *save_next = event_next (e2);

    *e2 = *e1;
    set_event_next (e2, save_next);
    return (event2);
  }
}



Lisp_Object QKbackspace, QKtab, QKlinefeed, QKreturn, QKescape,
 QKspace, QKdelete, QKnosymbol;

int
command_event_p (struct Lisp_Event *event)
{
  switch (event->event_type)
    {
    case key_press_event:
    case button_press_event:
    case button_release_event:
    case misc_user_event:
      return (1);
    default:
      return (0);
    }
}


void
character_to_event (Emchar c, struct Lisp_Event *event, struct device *d)
{
  Lisp_Object k = Qnil;
  unsigned int m = 0;
  if (event->event_type == dead_event)
    error ("character-to-event called with a deallocated event!");

#ifndef MULE
  c &= 255;
#endif
  if (c > 127 && c <= 255)
    {
      int meta_flag = 1;
      if (d && DEVICE_IS_TTY (d))
	meta_flag = TTY_FLAGS (d).meta_key;
      switch (meta_flag)
	{
	case 0: /* ignore top bit; it's parity */
	  c -= 128;
	  break;
	case 1: /* top bit is meta */
	  c -= 128;
	  m = MOD_META;
	  break;
	default: /* this is a real character */
	  break;
	}
    }
  if (c < ' ') c += '@', m |= MOD_CONTROL;
  if (m & MOD_CONTROL)
    {
      switch (c)
	{
	case 'I': k = QKtab;	  m &= ~MOD_CONTROL; break;
	case 'J': k = QKlinefeed; m &= ~MOD_CONTROL; break;
	case 'M': k = QKreturn;	  m &= ~MOD_CONTROL; break;
	case '[': k = QKescape;	  m &= ~MOD_CONTROL; break;
# if 0
	  /* This is probably too controversial... */
	case 'H': k = QKbackspace; m &= ~MOD_CONTROL; break;
# endif
	}
      if (c >= 'A' && c <= 'Z') c -= 'A'-'a';
    }
  else if (c == 127) k = QKdelete;
  else if (c == ' ') k = QKspace;
  
  event->event_type		= key_press_event;
  event->channel		= Qnil;
  event->timestamp		= 0;
  if (d)
    XSETDEVICE (event->device, d);
  else
    event->device = Fselected_device ();
  event->event.key.keysym	= (!NILP (k) ? k : make_number (c));
  event->event.key.modifiers	= m;
}


/* This variable controls what character name -> character code mapping
   we are using.  Window-system-specific code sets this to some symbol,
   and we use that symbol as the plist key to convert keysyms into 8-bit
   codes.  In this way one can have several character sets predefined and
   switch them by changing this.
 */
Lisp_Object Vcharacter_set_property;

Emchar
event_to_character (struct Lisp_Event *event,
		    int allow_extra_modifiers,
		    int allow_meta,
		    int allow_non_ascii)
{
  Emchar c = 0;
  if (event->event_type != key_press_event)
    {
      if (event->event_type == dead_event) abort ();
      return -1;
    }
  if (!allow_extra_modifiers &&
      event->event.key.modifiers & (MOD_SUPER|MOD_HYPER|MOD_ALT))
    return -1;
  if (INTP (event->event.key.keysym))   c = XINT (event->event.key.keysym);
  else if (EQ (event->event.key.keysym, QKbackspace))	c = '\b';
  else if (EQ (event->event.key.keysym, QKtab))		c = '\t';
  else if (EQ (event->event.key.keysym, QKlinefeed))	c = '\n';
  else if (EQ (event->event.key.keysym, QKreturn))	c = '\r';
  else if (EQ (event->event.key.keysym, QKescape))	c = 27;
  else if (EQ (event->event.key.keysym, QKspace))	c = ' ';
  else if (EQ (event->event.key.keysym, QKdelete))	c = 127;

  else if (!SYMBOLP (event->event.key.keysym))
    abort ();
  else if (allow_non_ascii && !NILP (Vcharacter_set_property))
    {
      /* Allow window-system-specific extensibility of keysym->code mapping */
      Lisp_Object code = Fget (event->event.key.keysym,
                               Vcharacter_set_property,
			       Qnil);
      if (!INTP (code))
	return -1;
      c = XINT (code);
    }
  else
    return -1;

  if (event->event.key.modifiers & MOD_CONTROL)
    {
      if (c >= 'a' && c <= 'z')
	c -= ('a' - 'A');
      else
	/* reject Control-Shift- keys */
	if (c >= 'A' && c <= 'Z' && !allow_extra_modifiers)
	  return -1;
      
      if (c >= '@' && c <= '_')
	c -= '@';
      else if (c == ' ')  /* C-space and C-@ are the same. */
	c = 0;
      else
	/* reject keys that can't take Control- modifiers */
	if (! allow_extra_modifiers) return -1;
    }

  if (event->event.key.modifiers & MOD_META)
    {
      if (! allow_meta) return -1;
      if (c & 0200) return -1;		/* don't allow M-oslash (overlap) */
#ifdef MULE
      if (c >= 256) return -1;
#endif
      c |= 0200;
    }
  return c;
}


DEFUN ("event-to-character", Fevent_to_character, Sevent_to_character,
       1, 4, 0,
 "Return the closest ASCII approximation to the given event object.\n\
If the event isn't a keypress, this returns nil.\n\
If the ALLOW-EXTRA-MODIFIERS argument is non-nil, then this is lenient in\n\
 its translation; it will ignore modifier keys other than control and meta,\n\
 and will ignore the shift modifier on those characters which have no\n\
 shifted ASCII equivalent (Control-Shift-A for example, will be mapped to\n\
 the same ASCII code as Control-A).\n\
If the ALLOW-META argument is non-nil, then the Meta modifier will be\n\
 represented by turning on the high bit of the byte returned; otherwise, nil\n\
 will be returned for events containing the Meta modifier.\n\
If the ALLOW-NON-ASCII argument is non-nil, then characters which are\n\
 present in the prevailing character set (see the `character-set-property'\n\
 variable) will be returned as their code in that character set, instead of\n\
 the return value being restricted to ASCII.\n\
Note that specifying both ALLOW-META and ALLOW-NON-ASCII is ambiguous, as\n\
 both use the high bit; `M-x' and `oslash' will be indistinguishable.")
     (event, allow_extra_modifiers, allow_meta, allow_non_ascii)
     Lisp_Object event, allow_extra_modifiers, allow_meta, allow_non_ascii;
{
  Emchar c;
  CHECK_LIVE_EVENT (event, 0);
  c = event_to_character (XEVENT (event),
			  !NILP (allow_extra_modifiers),
			  !NILP (allow_meta),
			  !NILP (allow_non_ascii));
  return (c < 0 ? Qnil : make_number (c));
}


DEFUN ("character-to-event", Fcharacter_to_event, Scharacter_to_event, 1, 3, 0,
  "Converts a numeric ASCII value to an event structure, replete with\n\
bucky bits.  The character is the first argument, and the event to fill\n\
in is the second.  This function contains knowledge about what the codes\n\
``mean'' -- for example, the number 9 is converted to the character ``Tab'',\n\
not the distinct character ``Control-I''.\n\
\n\
Note that CH does not have to be a numeric value, but can be a symbol such\n\
 as 'clear or a list such as '(control backspace).\n\
\n\
If the optional second argument is an event, it is modified; otherwise, a\n\
 new event object is created.\n\
\n\
Optional third arg DEVICE is the device to store in the event; this also\n\
 affects whether the high bit is interpreted as a meta key.  A value of nil\n\
 means use the selected device but always treat the high bit as meta.\n\
\n\
Beware that character-to-event and event-to-character are not strictly\n\
 inverse functions, since events contain much more information than the\n\
 ASCII character set can encode.")
     (ch, event, device)
     Lisp_Object ch, event, device;
{
  struct device *d;

  if (EQ (device, Qnil))
    d = 0;
  else
    d = get_device (device);
  if (NILP (event))
    event = Fallocate_event ();
  else
    CHECK_LIVE_EVENT (event, 0);
  if (CHARP (ch))
    character_to_event (XINT (ch), XEVENT (event), d);
  else if (CONSP (ch) || SYMBOLP (ch))
    key_desc_list_to_event (ch, event, 1);
  else
    CHECK_INT (ch, 0);
  return event;
}

void
format_event_object (char *buf, struct Lisp_Event *event, int brief)
{
  int mouse_p = 0;
  int mod = 0;
  Lisp_Object key;

  switch (event->event_type)
    {
    case key_press_event:
      {
        mod = event->event.key.modifiers;
        key = event->event.key.keysym;
        /* Hack. */
        if (! brief && INTP (key) &&
            mod & (MOD_CONTROL|MOD_META|MOD_SUPER|MOD_HYPER))
	{
	  int k = XINT (key);
	  if (k >= 'a' && k <= 'z')
	    key = make_number (k - ('a'-'A'));
	  else if (k >= 'A' && k <= 'Z')
	    mod |= MOD_SHIFT;
	}
        break;
      }
    case button_release_event:
      mouse_p++;
      /* Fall through */
    case button_press_event:
      {
        mouse_p++;
        mod = event->event.button.modifiers;
        key = make_number (event->event.button.button + '0');
        break;
      }
    case magic_event:
      {
        CONST char *name = 0;

#ifdef HAVE_X_WINDOWS
        if (DEVICE_IS_X (XDEVICE (Vselected_device)))
	  name =
	    x_event_name (event->event.magic.underlying_x_event.xany.type);
#endif
#ifdef HAVE_NEXTSTEP
        if (DEVICE_IS_NS (XDEVICE (Vselected_device)))
	  name = ns_event_name (event->event.magic.underlying_ns_event);
#endif
	if (name) strcpy (buf, name);
	else strcpy (buf, "???");
	return;
      }
    case pointer_motion_event:	strcpy (buf, "motion");	return;
    case misc_user_event:	strcpy (buf, "misc-user"); return;
    case eval_event:		strcpy (buf, "eval"); 	return;
    case process_event:		strcpy (buf, "process");return;
    case timeout_event:		strcpy (buf, "timeout");return;
    case empty_event:		strcpy (buf, "EMPTY-EVENT"); return;
    case dead_event:		strcpy (buf, "DEAD-EVENT");  return;
    default:
      abort ();
    }
#define modprint1(x)  { strcpy (buf, (x)); buf += sizeof (x)-1; }
#define modprint(x,y) { if (brief) modprint1 (y) else modprint1 (x) }
  if (mod & MOD_CONTROL) modprint ("control-", "C-");
  if (mod & MOD_META)    modprint ("meta-",    "M-");
  if (mod & MOD_SUPER)   modprint ("super-",   "S-");
  if (mod & MOD_HYPER)   modprint ("hyper-",   "H-");
  if (mod & MOD_ALT)	 modprint ("alt-",     "A-");
  if (mod & MOD_SHIFT)   modprint ("shift-",   "Sh-");
  if (mouse_p)
    {
      modprint1 ("button");
      --mouse_p;
    }
#undef modprint
#undef modprint1

  if (INTP (key))
    {
      Bytecount len = emchar_to_charptr (XINT (key), (Bufbyte *) buf);
      buf[len] = 0;
      buf += len;
    }
  else if (SYMBOLP (key))
    {
      CONST char *str = 0;
      if (brief)
	{
	  if (EQ (key, QKlinefeed)) str = "LFD";
	  else if (EQ (key, QKtab)) str = "TAB";
	  else if (EQ (key, QKreturn)) str = "RET";
	  else if (EQ (key, QKescape)) str = "ESC";
	  else if (EQ (key, QKdelete)) str = "DEL";
	  else if (EQ (key, QKspace)) str = "SPC";
	  else if (EQ (key, QKbackspace)) str = "BS";
	}
      if (str)
	{
	  int i = strlen (str);
	  memcpy (buf, str, i+1);
	  str += i;
	}
      else
	{
	  memcpy (buf, string_data (XSYMBOL (key)->name),
                string_length (XSYMBOL (key)->name) + 1);
	  str += string_length (XSYMBOL (key)->name);
	}
    }
  else
    abort ();
  if (mouse_p)
    strncpy (buf, "up", 4);
}

DEFUN ("eventp", Feventp, Seventp, 1, 1, 0,
       "True if OBJECT is an event object.")
     (object)
     Lisp_Object object;
{
  return ((EVENTP (object)) ? Qt : Qnil);
}

DEFUN ("event-live-p", Fevent_live_p, Sevent_live_p, 1, 1, 0,
  "True if OBJECT is an event object that has not been deallocated.")
     (object)
     Lisp_Object object;
{
  return ((EVENTP (object) && XEVENT (object)->event_type != dead_event)
	  ? Qt : Qnil);
}

/* DEFUN ("event-next", Fevent_next, Sevent_next, 1, 1, 0,
 *   "Return the event object's `next' event, or nil if it has none.\n\
 * The `next-event' field is changed by calling `set-next-event'.")
 *      (event)
 *      Lisp_Object event;
 * {
 *   struct Lisp_Event *e;
 *   CHECK_LIVE_EVENT (event, 0);
 * 
 *   e = event_next (XEVENT (event));
 *   if (!e)
 *     return Qnil;
 *   XSETEVENT (event, e);
 *   return (event);
 * }
 * 
 * DEFUN ("set-event-next", Fset_event_next, Sset_event_next, 2, 2, 0,
 *   "Set the `next event' of EVENT to NEXT-EVENT.\n\
 * NEXT-EVENT must be an event object or nil.")
 *      (event, next_event)
 *      Lisp_Object event, next_event;
 * {
 *   struct Lisp_Event *e;
 * 
 *   CHECK_LIVE_EVENT (event, 0);
 *   if (NILP (next_event))
 *     {
 *       event_next (XEVENT (event)) = 0;
 *       return (Qnil);
 *     }
 * 
 *   CHECK_LIVE_EVENT (next_event, 1);
 *   for (e = XEVENT (next_event); e; e = event_next (e))
 *     {
 *       QUIT;
 *       if (e == XEVENT (event))
 * 	signal_error (Qerror, 
 * 		      list3 (build_string ("Cyclic event-next"),
 * 			     event, 
 * 			     next_event));
 *     }
 *   event_next (XEVENT (event)) = XEVENT (next_event);
 *   return (next_event);
 * }
 */


#define EVENT_PRED(type) \
  return ((EVENTP (obj) && XEVENT (obj)->event_type == (type)) \
	  ? Qt : Qnil)

DEFUN ("key-press-event-p", Fkey_press_event_p, Skey_press_event_p, 1, 1, 0,
       "True if the argument is a key-press event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (key_press_event);
}

DEFUN ("button-press-event-p", Fbutton_press_event_p, Sbutton_press_event_p,
       1, 1, 0, "True if the argument is a mouse-button-press event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (button_press_event);
}

DEFUN ("button-release-event-p", Fbutton_release_event_p,
       Sbutton_release_event_p, 1, 1, 0,
       "True if the argument is a mouse-button-release event object.")
     (obj)
     Lisp_Object obj;
{
  if (EVENTP (obj) && XEVENT (obj)->event_type == button_release_event)
    return Qt;
  else
    return Qnil;
}  
/* { EVENT_PRED (button_release_event); } */

DEFUN ("button-event-p", Fbutton_event_p,
       Sbutton_event_p, 1, 1, 0,
       "True if the argument is a button-press or button-release event object.")
     (obj)
     Lisp_Object obj;
{
  return ((EVENTP (obj)
	   && (XEVENT (obj)->event_type == button_press_event ||
	       XEVENT (obj)->event_type == button_release_event))
	  ? Qt : Qnil);
}

DEFUN ("motion-event-p", Fmotion_event_p, Smotion_event_p, 1, 1, 0,
       "True if the argument is a mouse-motion event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (pointer_motion_event);
}

DEFUN ("process-event-p", Fprocess_event_p, Sprocess_event_p, 1, 1, 0,
       "True if the argument is a process-output event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (process_event);
}

DEFUN ("timeout-event-p", Ftimeout_event_p, Stimeout_event_p, 1, 1, 0,
       "True if the argument is a timeout event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (timeout_event);
}

DEFUN ("misc-user-event-p", Fmisc_user_event_p, Smisc_user_event_p, 1, 1, 0,
       "True if the argument is a misc-user event object.\n\
A misc-user event is a user event that is not a keypress or mouse click;\n\
normally this means a menu selection or scrollbar action.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (misc_user_event);
}

DEFUN ("eval-event-p", Feval_event_p, Seval_event_p, 1, 1, 0,
       "True if the argument is an `eval' event object.")
     (obj)
     Lisp_Object obj;
{
  EVENT_PRED (eval_event);
}

DEFUN ("event-timestamp", Fevent_timestamp, Sevent_timestamp, 1, 1, 0,
  "Return the timestamp of the given event object.")
     (event)
  Lisp_Object event;
{
  CHECK_LIVE_EVENT (event, 0);
  /* This junk is so that timestamps don't get to be negative, but contain
     as many bits as this particular emacs will allow.
   */
  return make_number (((1L << (VALBITS - 1)) - 1) &
		      XEVENT (event)->timestamp);
}

#define CHECK_EVENT_TYPE(e,t1,sym)		\
{ CHECK_LIVE_EVENT (e, 0);			\
  if (XEVENT(e)->event_type != (t1))		\
     e = wrong_type_argument ((sym),(e));	\
}

#define CHECK_EVENT_TYPE2(e,t1,t2,sym)					\
{ CHECK_LIVE_EVENT (e, 0);						\
  if (XEVENT(e)->event_type != (t1) && XEVENT(e)->event_type != (t2))	\
     e = wrong_type_argument ((sym),(e));				\
}

DEFUN ("event-key", Fevent_key, Sevent_key, 1, 1, 0,
       "Return the KeySym of the given key-press event.  This will be the\n\
ASCII code of a printing character, or a symbol.")
     (event)
  Lisp_Object event;
{
  CHECK_EVENT_TYPE (event, key_press_event, Qkey_press_event_p);
  return (XEVENT (event)->event.key.keysym);
}

/* #### This is X only but we need to redo some lisp code to get rid of it */

DEFUN ("event-button", Fevent_button, Sevent_button, 1, 1, 0,
       "Return the button-number of the given mouse-button-press event.")
     (event)
  Lisp_Object event;
{
  CHECK_EVENT_TYPE2 (event, button_press_event, button_release_event,
		     Qbutton_event_p);
#ifdef HAVE_WINDOW_SYSTEM
  return make_number (XEVENT (event)->event.button.button);
#else /* !HAVE_WINDOW_SYSTEM */
  return Qzero;
#endif /* !HAVE_WINDOW_SYSTEM */
}

DEFUN ("event-modifier-bits", Fevent_modifier_bits, Sevent_modifier_bits,
       1, 1, 0,
       "Return a number representing the modifier keys which were down\n\
when the given mouse or keyboard event was produced.  See also the function\n\
event-modifiers.")
     (event)
  Lisp_Object event;
{
 again:
  CHECK_LIVE_EVENT (event, 0);
  if (XEVENT (event)->event_type == key_press_event)
    return make_number (XEVENT (event)->event.key.modifiers);
  else if (XEVENT (event)->event_type == button_press_event ||
	   XEVENT (event)->event_type == button_release_event)
    return make_number (XEVENT (event)->event.button.modifiers);
  else if (XEVENT (event)->event_type == pointer_motion_event)
    return make_number (XEVENT (event)->event.motion.modifiers);
  else
    {
      event = wrong_type_argument (intern ("key-or-mouse-event-p"), event);
      goto again;
    }
}

DEFUN ("event-modifiers", Fevent_modifiers, Sevent_modifiers, 1, 1, 0,
       "Return a list of symbols, the names of the modifier keys\n\
which were down when the given mouse or keyboard event was produced.\n\
See also the function event-modifier-bits.")
     (event)
  Lisp_Object event;
{
  int mod = XINT (Fevent_modifier_bits (event));
  Lisp_Object result = Qnil;
  if (mod & MOD_SHIFT)   result = Fcons (Qshift, result);
  if (mod & MOD_ALT)	 result = Fcons (Qalt, result);
  if (mod & MOD_HYPER)   result = Fcons (Qhyper, result);
  if (mod & MOD_SUPER)   result = Fcons (Qsuper, result);
  if (mod & MOD_META)    result = Fcons (Qmeta, result);
  if (mod & MOD_CONTROL) result = Fcons (Qcontrol, result);
  return result;
}

static int
event_x_y_pixel_internal (Lisp_Object event, int *x, int *y, int relative)
{
  struct window *w;
  struct frame *f;

  if (XEVENT (event)->event_type == pointer_motion_event)
    {
      *x = XEVENT (event)->event.motion.x;
      *y = XEVENT (event)->event.motion.y;
    }
  else if (XEVENT (event)->event_type == button_press_event ||
	   XEVENT (event)->event_type == button_release_event)
    {
      *x = XEVENT (event)->event.button.x;
      *y = XEVENT (event)->event.button.y;
    }
  else
    return 0;

  f = XFRAME (Fevent_frame (event));

  if (relative)
    {
      w = find_window_by_pixel_pos (*x, *y, f->root_window);

      if (!w)
	return 1;	/* #### What should really happen here. */

      *x -= w->pixel_left;
      *y -= w->pixel_top;
    }
  else
    {
      *y -= FRAME_REAL_TOP_TOOLBAR_HEIGHT (f);
      *x -= FRAME_REAL_LEFT_TOOLBAR_WIDTH (f);
    }

  return 1;
}

DEFUN ("event-window-x-pixel", Fevent_window_x_pixel, Sevent_window_x_pixel,
       1, 1, 0,
 "Return the X position in pixels of the given mouse event.\n\
The value returned is relative to the window the event occurred in.\n\
This will signal an error if the event is not a mouse-motion, button-press,\n\
or button-release event.  See also `event-x-pixel'.")
     (event)
     Lisp_Object event;
{
  int x, y;

  CHECK_LIVE_EVENT (event, 0);

  if (!event_x_y_pixel_internal (event, &x, &y, 1))
    return wrong_type_argument (Qmouse_event_p, event);
  else
    return make_number (x);
}

DEFUN ("event-window-y-pixel", Fevent_window_y_pixel, Sevent_window_y_pixel,
       1, 1, 0,
 "Return the Y position in pixels of the given mouse event.\n\
The value returned is relative to the window the event occurred in.\n\
This will signal an error if the event is not a mouse-motion, button-press,\n\
or button-release event.  See also `event-y-pixel'.")
     (event)
     Lisp_Object event;
{
  int x, y;

  CHECK_LIVE_EVENT (event, 0);

  if (!event_x_y_pixel_internal (event, &x, &y, 1))
    return wrong_type_argument (Qmouse_event_p, event);
  else
    return make_number (y);
}

DEFUN ("event-x-pixel", Fevent_x_pixel, Sevent_x_pixel,
       1, 1, 0,
 "Return the X position in pixels of the given mouse event.\n\
The value returned is relative to the frame the event occurred in.\n\
This will signal an error if the event is not a mouse-motion, button-press,\n\
or button-release event.  See also `event-window-x-pixel'.")
     (event)
     Lisp_Object event;
{
  int x, y;

  CHECK_LIVE_EVENT (event, 0);

  if (!event_x_y_pixel_internal (event, &x, &y, 0))
    return wrong_type_argument (Qmouse_event_p, event);
  else
    return make_number (x);
}

DEFUN ("event-y-pixel", Fevent_y_pixel, Sevent_y_pixel,
       1, 1, 0,
 "Return the Y position in pixels of the given mouse event.\n\
The value returned is relative to the frame the event occurred in.\n\
This will signal an error if the event is not a mouse-motion, button-press,\n\
or button-release event.  See also `event-window-y-pixel'.")
     (event)
     Lisp_Object event;
{
  int x, y;

  CHECK_LIVE_EVENT (event, 0);

  if (!event_x_y_pixel_internal (event, &x, &y, 0))
    return wrong_type_argument (Qmouse_event_p, event);
  else
    return make_number (y);
}

static int
event_pixel_translation (Lisp_Object event, int *char_x, int *char_y,
			 int *obj_x, int *obj_y,
			 struct window **w, Bytind *bufp, Bytind *closest,
			 Lisp_Object *obj)
{
  int pix_x = 0;
  int pix_y = 0;
  int result;
  Lisp_Object frame;

  int ret_x, ret_y, ret_obj_x, ret_obj_y;
  struct window *ret_w;
  Bytind ret_bufp, ret_closest;
  Lisp_Object ret_obj;
  
  CHECK_LIVE_EVENT (event, 0);
  if (XEVENT (event)->event_type == pointer_motion_event)
    {
      pix_x = XEVENT (event)->event.motion.x;
      pix_y = XEVENT (event)->event.motion.y;
      frame = XEVENT (event)->channel;
    }
  else if (XEVENT (event)->event_type == button_press_event ||
	   XEVENT (event)->event_type == button_release_event)
    {
      pix_x = XEVENT (event)->event.button.x;
      pix_y = XEVENT (event)->event.button.y;
      frame = XEVENT (event)->channel;
    }
  else
    wrong_type_argument (Qmouse_event_p, event);

  result = pixel_to_glyph_translation (XFRAME (frame), pix_x, pix_y,
				       &ret_x, &ret_y, &ret_obj_x, &ret_obj_y,
				       &ret_w, &ret_bufp, &ret_closest,
				       &ret_obj);

  /* pixel_to_glyph_translation returns the following values:

     OVER_TOOLBAR:	over one of the 4 frame toolbars
     OVER_MODELINE:	over a modeline
     OVER_BORDER:	over an internal border
     OVER_NOTHING:	over the text area, but not over text
     OVER_OUTSIDE:	outside of the frame border
     OVER_TEXT:		over text in the text area
     */

  if (result == OVER_NOTHING || result == OVER_OUTSIDE)
    ret_bufp = 0;
  else if (ret_w && NILP (ret_w->buffer))
    /* Why does this happen?  (Does it still happen?)
       I guess the window has gotten reused as a non-leaf... */
    ret_w = 0;

  /* #### pixel_to_glyph_translation() sometimes returns garbage...
     The word has type Lisp_Record (presumably meaning `extent') but the
     pointer points to random memory, often filled with 0, sometimes not.
   */
  if (!NILP (ret_obj) && !(EXTENTP (ret_obj) || TOOLBAR_BUTTONP (ret_obj)))
    abort ();

  if (char_x)
    *char_x = ret_x;
  if (char_y)
    *char_y = ret_y;
  if (obj_x)
    *obj_x = ret_obj_x;
  if (obj_y)
    *obj_y = ret_obj_y;
  if (w)
    *w = ret_w;
  if (bufp)
    *bufp = ret_bufp;
  if (closest)
    *closest = ret_closest;
  if (obj)
    *obj = ret_obj;

  return result;
}

DEFUN ("event-over-text-area-p", Fevent_over_text_area_p,
       Sevent_over_text_area_p, 1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event, return\n\
t if the event is over the text area of a window.  Otherwise, return\n\
nil.  The modeline is not considered to be part of the text area.")
     (event)
     Lisp_Object event;
{
  int result = event_pixel_translation (event, 0, 0, 0, 0, 0, 0, 0, 0);

  if (result == OVER_TEXT || result == OVER_NOTHING)
    return Qt;
  else
    return Qnil;
}

DEFUN ("event-over-modeline-p", Fevent_over_modeline_p, Sevent_over_modeline_p,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event, return\n\
t if the event is over the modeline of a window.  Otherwise, return nil.")
     (event)
     Lisp_Object event;
{
  int result = event_pixel_translation (event, 0, 0, 0, 0, 0, 0, 0, 0);

  if (result == OVER_MODELINE)
    return Qt;
  else
    return Qnil;
}

DEFUN ("event-over-border-p", Fevent_over_border_p, Sevent_over_border_p,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event, return\n\
t if the event is over an internal border.  Otherwise, return nil.")
     (event)
     Lisp_Object event;
{
  int result = event_pixel_translation (event, 0, 0, 0, 0, 0, 0, 0, 0);

  if (result == OVER_BORDER)
    return Qt;
  else
    return Qnil;
}

DEFUN ("event-over-toolbar-p", Fevent_over_toolbar_p, Sevent_over_toolbar_p,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event, return\n\
t if the event is over a toolbar.  Otherwise, return nil.")
     (event)
     Lisp_Object event;
{
  int result = event_pixel_translation (event, 0, 0, 0, 0, 0, 0, 0, 0);

  if (result == OVER_TOOLBAR)
    return Qt;
  else
    return Qnil;
}

DEFUN ("event-device", Fevent_device, Sevent_device, 1, 1, 0,
 "Return the device that the given event occurred on.\n\
This will be nil for some types of events (e.g. eval events).")
     (event)
     Lisp_Object event;
{
  CHECK_LIVE_EVENT (event, 0);
  return EVENT_DEVICE (XEVENT (event));
}

/* It would be possible to just use (window-frame (event-window event))
   but this gives better encapsulation and often makes the code easier
   to understand. */
DEFUN ("event-frame", Fevent_frame, Sevent_frame, 1, 1, 0,
 "Given a mouse-motion, button-press, or button-release event, return\n\
the frame on which that event occurred.  This will be nil for non-mouse\n\
events.")
     (event)
     Lisp_Object event;
{
  CHECK_LIVE_EVENT (event, 0);
  if (FRAMEP (XEVENT (event)->channel))
    return (XEVENT (event)->channel);
  else
    return Qnil;
}

DEFUN ("event-window", Fevent_window, Sevent_window, 1, 1, 0,
 "Given a mouse motion, button press, or button release event, compute\n\
and return the window on which that event occurred.  This may be nil if\n\
the event occurred in the border or over a toolbar.  The modeline is\n\
considered to be in the window it represents.")
     (event)
     Lisp_Object event;
{
  struct window *w;
  Lisp_Object window;

  event_pixel_translation (event, 0, 0, 0, 0, &w, 0, 0, 0);

  if (!w)
    return Qnil;
  else
    {
      XSETWINDOW (window, w);
      return window;
    }
}

/* It would be possible to just use (window-buffer (event-window event))
   but this gives better encapsulation and often makes the code easier
   to understand. */
DEFUN ("event-buffer", Fevent_buffer, Sevent_buffer, 1, 1, 0,
 "Given a mouse-motion, button-press, or button-release event, return\n\
the buffer on which that event occurred.  This will be nil for non-mouse\n\
events.  If event-over-text-area-p is nil, this will also be nil.")
     (event)
     Lisp_Object event;
{
  Lisp_Object window = Fevent_window (event);

  if (WINDOWP (window))
    return XWINDOW (window)->buffer;
  else
    return Qnil;
}

DEFUN ("event-point", Fevent_point, Sevent_point, 1, 1, 0,
 "Return the character position of the given mouse-motion, button-press,\n\
or button-release event.  If the event did not occur over a window, or did\n\
not occur over text, then this returns nil.  Otherwise, it returns an index\n\
into the buffer visible in the event's window.")
     (event)
     Lisp_Object event;
{
  Bytind bufp;
  struct window *w;

  event_pixel_translation (event, 0, 0, 0, 0, &w, &bufp, 0, 0);

  if (!w)
    return Qnil;
  else if (!bufp)
    return Qnil;
  else
    return make_number (bufp);
}

DEFUN ("event-closest-point", Fevent_closest_point, Sevent_closest_point,
       1, 1, 0,
 "Return the character position of the given mouse-motion, button-press,\n\
or button-release event.  If the event did not occur over a window or over\n\
text, return the closest point to the location of the event.  If the Y pixel\n\
position overlaps a window and the X pixel position is to the left of that\n\
window, the closest point is the beginning of the line containing the\n\
Y position.  If the Y pixel position overlaps a window and the X pixel\n\
position is to the right of that window, the closest point is the end of the\n\
line containing the Y position.  If the Y pixel position is above a window,\n\
return 0.  If it is below a window, return the value of (window-end).")
     (event)
     Lisp_Object event;
{
  Bytind bufp;

  event_pixel_translation (event, 0, 0, 0, 0, 0, 0, &bufp, 0);

  if (!bufp)
    return Qnil;
  else
    return make_number (bufp);
}

DEFUN ("event-x", Fevent_x, Sevent_x, 1, 1, 0,
 "Return the X position of the given mouse-motion, button-press, or\n\
button-release event in characters.  This is relative to the window the\n\
event occurred over.")
     (event)
     Lisp_Object event;
{
  int char_x;

  event_pixel_translation (event, &char_x, 0, 0, 0, 0, 0, 0, 0);

  return make_number (char_x);
}

DEFUN ("event-y", Fevent_y, Sevent_y, 1, 1, 0,
 "Return the Y position of the given mouse-motion, button-press, or\n\
button-release event in characters.  This is relative to the window the\n\
event occurred over.")
     (event)
     Lisp_Object event;
{
  int char_y;

  event_pixel_translation (event, 0, &char_y, 0, 0, 0, 0, 0, 0);

  return make_number (char_y);
}


DEFUN ("event-glyph-extent", Fevent_glyph_extent, Sevent_glyph_extent, 1, 1, 0,
 "If the given mouse-motion, button-press, or button-release event happened\n\
on top of a glyph, this returns its extent.  Otherwise return nil.")
     (event)
     Lisp_Object event;
{
  Lisp_Object extent;
  struct window *w;

  event_pixel_translation (event, 0, 0, 0, 0, &w, 0, 0, &extent);

  if (!w)
    return Qnil;
  else if (EXTENTP (extent))
    return extent;
  else
    return Qnil;
}

/* You could just as easily use event-glyph-extent but we include this
   for consistency.  It could easily be implemented in elisp but none
   of the rest of the event- functions are so we'll just stick it
   here. */
DEFUN ("event-over-glyph-p", Fevent_over_glyph_p, Sevent_over_glyph_p,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event, return\n\
t if the event is over a glyph.  Otherwise, return nil.")
     (event)
     Lisp_Object event;
{
  if (NILP (Fevent_glyph_extent (event)))
    return Qnil;
  else
    return Qt;
}

DEFUN ("event-glyph-x-pixel", Fevent_glyph_x_pixel, Sevent_glyph_x_pixel,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event over a glyph,\n\
return the X position of the pointer relative to the upper left of the\n\
glyph.  If the event is not over a glyph, return nil.")
     (event)
     Lisp_Object event;
{
  Lisp_Object extent;
  struct window *w;
  int obj_x;

  event_pixel_translation (event, 0, 0, &obj_x, 0, &w, 0, 0, &extent);

  if (w && EXTENTP (extent))
    return make_number (obj_x);
  else
    return Qnil;
}

DEFUN ("event-glyph-y-pixel", Fevent_glyph_y_pixel, Sevent_glyph_y_pixel,
       1, 1, 0,
  "Given a mouse-motion, button-press, or button-release event over a glyph,\n\
return the Y position of the pointer relative to the upper left of the\n\
glyph.  If the event is not over a glyph, return nil.")
     (event)
     Lisp_Object event;
{
  Lisp_Object extent;
  struct window *w;
  int obj_y;

  event_pixel_translation (event, 0, 0, 0, &obj_y, &w, 0, 0, &extent);

  if (w && EXTENTP (extent))
    return make_number (obj_y);
  else
    return Qnil;
}

DEFUN ("event-toolbar-button", Fevent_toolbar_button, Sevent_toolbar_button,
       1, 1, 0,
  "If the given mouse-motion, button-press, or button-release event happened\n\
on top of a toolbar button, return the button.  Otherwise, return nil.")
     (event)
     Lisp_Object event;
{
  Lisp_Object button;
  int result;

  result = event_pixel_translation (event, 0, 0, 0, 0, 0, 0, 0, &button);

  if (result == OVER_TOOLBAR)
    {
      if (TOOLBAR_BUTTONP (button))
	return button;
      else
	return Qnil;
    }
  else
    return Qnil;
}

DEFUN ("event-process", Fevent_process, Sevent_process, 1, 1, 0,
 "Return the process of the given process-output event.")
     (event)
  Lisp_Object event;
{
  CHECK_EVENT_TYPE (event, process_event, Qprocess_event_p);
  return (XEVENT (event)->event.process.process);
}

DEFUN ("event-function", Fevent_function, Sevent_function, 1, 1, 0,
 "Return the callback function of the given timeout, misc-user, or eval event.")
     (event)
  Lisp_Object event;
{
  CHECK_LIVE_EVENT (event, 0);
  switch (XEVENT (event)->event_type)
    {
    case timeout_event:
      return (XEVENT (event)->event.timeout.function);
    case misc_user_event:
    case eval_event:
      return (XEVENT (event)->event.eval.function);
    default:
      return wrong_type_argument (intern ("timeout-or-eval-event-p"), event);
    }
}

DEFUN ("event-object", Fevent_object, Sevent_object, 1, 1, 0,
 "Return the callback function argument of the given timeout, misc-user, or\n\
eval event.")
     (event)
  Lisp_Object event;
{
 again:
  CHECK_LIVE_EVENT (event, 0);
  switch (XEVENT (event)->event_type)
    {
    case timeout_event:
      return (XEVENT (event)->event.timeout.object);
    case misc_user_event:
    case eval_event:
      return (XEVENT (event)->event.eval.object);
    default:
      event = wrong_type_argument (intern ("timeout-or-eval-event-p"), event);
      goto again;
    }
}


/************************************************************************/
/*                            initialization                            */
/************************************************************************/

void
syms_of_events (void)
{
  defsubr (&Scharacter_to_event);
  defsubr (&Sevent_to_character);

  defsubr (&Sallocate_event);
  defsubr (&Sdeallocate_event);
  defsubr (&Scopy_event);
  defsubr (&Seventp);
  defsubr (&Sevent_live_p);
  defsubr (&Skey_press_event_p);
  defsubr (&Sbutton_press_event_p);
  defsubr (&Sbutton_release_event_p);
  defsubr (&Sbutton_event_p);
  defsubr (&Smotion_event_p);
  defsubr (&Sprocess_event_p);
  defsubr (&Stimeout_event_p);
  defsubr (&Smisc_user_event_p);
  defsubr (&Seval_event_p);

  defsubr (&Sevent_timestamp);
  defsubr (&Sevent_key);
  defsubr (&Sevent_button);
  defsubr (&Sevent_modifier_bits);
  defsubr (&Sevent_modifiers);
  defsubr (&Sevent_x_pixel);
  defsubr (&Sevent_y_pixel);
  defsubr (&Sevent_window_x_pixel);
  defsubr (&Sevent_window_y_pixel);
  defsubr (&Sevent_over_text_area_p);
  defsubr (&Sevent_over_modeline_p);
  defsubr (&Sevent_over_border_p);
  defsubr (&Sevent_over_toolbar_p);
  defsubr (&Sevent_device);
  defsubr (&Sevent_frame);
  defsubr (&Sevent_window);
  defsubr (&Sevent_buffer);
  defsubr (&Sevent_point);
  defsubr (&Sevent_closest_point);
  defsubr (&Sevent_x);
  defsubr (&Sevent_y);
  defsubr (&Sevent_glyph_extent);
  defsubr (&Sevent_over_glyph_p);
  defsubr (&Sevent_glyph_x_pixel);
  defsubr (&Sevent_glyph_y_pixel);
  defsubr (&Sevent_toolbar_button);
  defsubr (&Sevent_process);
  defsubr (&Sevent_function);
  defsubr (&Sevent_object);

  defsymbol (&Qeventp, "eventp");
  defsymbol (&Qevent_live_p, "event-live-p");
  defsymbol (&Qkey_press_event_p, "key-press-event-p");
  defsymbol (&Qbutton_event_p, "button-event-p");
  defsymbol (&Qmouse_event_p, "mouse-event-p");
  defsymbol (&Qprocess_event_p, "process-event-p");
}

void
vars_of_events (void)
{
  DEFVAR_LISP ("character-set-property", &Vcharacter_set_property,
   "A symbol used to look up the 8-bit character of a keysym.\n\
To convert a keysym symbol to an 8-bit code, as when that key is\n\
bound to self-insert-command, we will look up the property that this\n\
variable names on the property list of the keysym-symbol.  The window-\n\
system-specific code will set up appropriate properties and set this\n\
variable.");
  Vcharacter_set_property = Qnil;

  event_resource = 0;

  QKbackspace = KEYSYM ("backspace");
  QKtab       = KEYSYM ("tab");
  QKlinefeed  = KEYSYM ("linefeed");
  QKreturn    = KEYSYM ("return");
  QKescape    = KEYSYM ("escape");
  QKspace     = KEYSYM ("space");
  QKdelete    = KEYSYM ("delete");
  QKnosymbol  = KEYSYM ("NoSymbol");

  staticpro (&QKbackspace);
  staticpro (&QKtab);
  staticpro (&QKlinefeed);
  staticpro (&QKreturn);
  staticpro (&QKescape);
  staticpro (&QKspace);
  staticpro (&QKdelete);
  staticpro (&QKnosymbol);
}
