view src/gpmevent.c @ 853:2b6fa2618f76

[xemacs-hg @ 2002-05-28 08:44:22 by ben] merge my stderr-proc ws make-docfile.c: Fix places where we forget to check for EOF. code-init.el: Don't use CRLF conversion by default on process output. CMD.EXE and friends work both ways but Cygwin programs don't like the CRs. code-process.el, multicast.el, process.el: Removed. Improvements to call-process-internal: -- allows a buffer to be specified for input and stderr output -- use it on all systems -- implement C-g as documented -- clean up and comment call-process-region uses new call-process facilities; no temp file. remove duplicate funs in process.el. comment exactly how coding systems work and fix various problems. open-multicast-group now does similar coding-system frobbing to open-network-stream. dumped-lisp.el, faces.el, msw-faces.el: Fix some hidden errors due to code not being defined at the right time. xemacs.mak: Add -DSTRICT. ================================================================ ALLOW SEPARATION OF STDOUT AND STDERR IN PROCESSES ================================================================ Standard output and standard error can be processed separately in a process. Each can have its own buffer, its own mark in that buffer, and its filter function. You can specify a separate buffer for stderr in `start-process' to get things started, or use the new primitives: set-process-stderr-buffer process-stderr-buffer process-stderr-mark set-process-stderr-filter process-stderr-filter Also, process-send-region takes a 4th optional arg, a buffer. Currently always uses a pipe() under Unix to read the error output. (#### Would a PTY be better?) sysdep.h, sysproc.h, unexfreebsd.c, unexsunos4.c, nt.c, emacs.c, callproc.c, symsinit.h, sysdep.c, Makefile.in.in, process-unix.c: Delete callproc.c. Move child_setup() to process-unix.c. wait_for_termination() now only needed on a few really old systems. console-msw.h, event-Xt.c, event-msw.c, event-stream.c, event-tty.c, event-unixoid.c, events.h, process-nt.c, process-unix.c, process.c, process.h, procimpl.h: Rewrite the process methods to handle a separate channel for error input. Create Lstreams for reading in the error channel. Many process methods need change. In general the changes are fairly clear as they involve duplicating what's used for reading the normal stdout and changing for stderr -- although tedious, as such changes are required throughout the entire process code. Rewrote the code that reads process output to do two loops, one for stdout and one for stderr. gpmevent.c, tooltalk.c: set_process_filter takes an argument for stderr. ================================================================ NEW ERROR-TRAPPING MECHANISM ================================================================ Totally rewrite error trapping code to be unified and support more features. Basic function is call_trapping_problems(), which lets you specify, by means of flags, what sorts of problems you want trapped. these can include -- quit -- errors -- throws past the function -- creation of "display objects" (e.g. buffers) -- deletion of already-existing "display objects" (e.g. buffers) -- modification of already-existing buffers -- entering the debugger -- gc -- errors->warnings (ala suspended errors) etc. All other error funs rewritten in terms of this one. Various older mechanisms removed or rewritten. window.c, insdel.c, console.c, buffer.c, device.c, frame.c: When creating a display object, added call to note_object_created(), for use with trapping_problems mechanism. When deleting, call check_allowed_operation() and note_object deleted(). The trapping-problems code records the objects created since the call-trapping-problems began. Those objects can be deleted, but none others (i.e. previously existing ones). bytecode.c, cmdloop.c: internal_catch takes another arg. eval.c: Add long comments describing the "five lists" used to maintain state (backtrace, gcpro, specbind, etc.) in the Lisp engine. backtrace.h, eval.c: Implement trapping-problems mechanism, eliminate old mechanisms or redo in terms of new one. frame.c, gutter.c: Flush out the concept of "critical display section", defined by the in_display() var. Use an internal_bind() to get it reset, rather than just doing it at end, because there may be a non-local exit. event-msw.c, event-stream.c, console-msw.h, device.c, dialog-msw.c, frame.c, frame.h, intl.c, toolbar.c, menubar-msw.c, redisplay.c, alloc.c, menubar-x.c: Make use of new trapping-errors stuff and rewrite code based on old mechanisms. glyphs-widget.c, redisplay.h: Protect calling Lisp in redisplay. insdel.c: Protect hooks against deleting existing buffers. frame-msw.c: Use EQ, not EQUAL in hash tables whose keys are just numbers. Otherwise we run into stickiness in redisplay because internal_equal() can QUIT. ================================================================ SIGNAL, C-G CHANGES ================================================================ Here we change the way that C-g interacts with event reading. The idea is that a C-g occurring while we're reading a user event should be read as C-g, but elsewhere should be a QUIT. The former code did all sorts of bizarreness -- requiring that no QUIT occurs anywhere in event-reading code (impossible to enforce given the stuff called or Lisp code invoked), and having some weird system involving enqueue/dequeue of a C-g and interaction with Vquit_flag -- and it didn't work. Now, we simply enclose all code where we want C-g read as an event with {begin/end}_dont_check_for_quit(). This completely turns off the mechanism that checks (and may remove or alter) C-g in the read-ahead queues, so we just get the C-g normal. Signal.c documents this very carefully. cmdloop.c: Correct use of dont_check_for_quit to new scheme, remove old out-of-date comments. event-stream.c: Fix C-g handling to actually work. device-x.c: Disable quit checking when err out. signal.c: Cleanup. Add large descriptive comment. process-unix.c, process-nt.c, sysdep.c: Use QUIT instead of REALLY_QUIT. It's not necessary to use REALLY_QUIT and just confuses the issue. lisp.h: Comment quit handlers. ================================================================ CONS CHANGES ================================================================ free_cons() now takes a Lisp_Object not the result of XCONS(). car and cdr have been renamed so that they don't get used directly; go through XCAR(), XCDR() instead. alloc.c, dired.c, editfns.c, emodules.c, fns.c, glyphs-msw.c, glyphs-x.c, glyphs.c, keymap.c, minibuf.c, search.c, eval.c, lread.c, lisp.h: Correct free_cons calling convention: now takes Lisp_Object, not Lisp_Cons chartab.c: Eliminate direct use of ->car, ->cdr, should be black box. callint.c: Rewrote using EXTERNAL_LIST_LOOP to avoid use of Lisp_Cons. ================================================================ USE INTERNAL-BIND-* ================================================================ eval.c: Cleanups of these funs. alloc.c, fileio.c, undo.c, specifier.c, text.c, profile.c, lread.c, redisplay.c, menubar-x.c, macros.c: Rewrote to use internal_bind_int() and internal_bind_lisp_object() in place of whatever varied and cumbersome mechanisms were formerly there. ================================================================ SPECBIND SANITY ================================================================ backtrace.h: - Improved comments backtrace.h, bytecode.c, eval.c: Add new mechanism check_specbind_stack_sanity() for sanity checking code each time the catchlist or specbind stack change. Removed older prototype of same mechanism. ================================================================ MISC ================================================================ lisp.h, insdel.c, window.c, device.c, console.c, buffer.c: Fleshed out authorship. device-msw.c: Correct bad Unicode-ization. print.c: Be more careful when not initialized or in fatal error handling. search.c: Eliminate running_asynch_code, an FSF holdover. alloc.c: Added comments about gc-cons-threshold. dialog-x.c: Use begin_gc_forbidden() around code to build up a widget value tree, like in menubar-x.c. gui.c: Use Qunbound not Qnil as the default for gethash. lisp-disunion.h, lisp-union.h: Added warnings on use of VOID_TO_LISP(). lisp.h: Use ERROR_CHECK_STRUCTURES to turn on ERROR_CHECK_TRAPPING_PROBLEMS and ERROR_CHECK_TYPECHECK lisp.h: Add assert_with_message. lisp.h: Add macros for gcproing entire arrays. (You could do this before but it required manual twiddling the gcpro structure.) lisp.h: Add prototypes for new functions defined elsewhere.
author ben
date Tue, 28 May 2002 08:45:36 +0000
parents e38acbeb1cae
children 804517e16990
line wrap: on
line source

/* GPM (General purpose mouse) functions
   Copyright (C) 1997 William M. Perry <wmperry@gnu.org>
   Copyright (C) 1999 Free Software Foundation, Inc.
   Copyright (C) 2002 Ben Wing.
   
   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, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

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

/* Authors: William Perry */

#include <config.h>
#include "lisp.h"

#include "commands.h"
#include "console-tty.h"
#include "console.h"
#include "device.h"
#include "events.h"
#include "lstream.h"
#include "process.h"
#include "sysdep.h"

#include "sysproc.h" /* for MAXDESC */

#ifdef HAVE_GPM
#include "gpmevent.h"
#include <gpm.h>

#define KG_SHIFT	0
#define KG_CTRL		2
#define KG_ALT		3

extern int gpm_tried;
extern void *gpm_stack;

static int (*orig_event_pending_p) (int);
static void (*orig_next_event_cb) (Lisp_Event *);

static Lisp_Object gpm_event_queue;
static Lisp_Object gpm_event_queue_tail;

struct __gpm_state
{
  int gpm_tried;
  int gpm_flag;
  void *gpm_stack;
};

static struct __gpm_state gpm_state_information[MAXDESC];

static void
store_gpm_state (int fd)
{
  gpm_state_information[fd].gpm_tried = gpm_tried;
  gpm_state_information[fd].gpm_flag = gpm_flag;
  gpm_state_information[fd].gpm_stack = gpm_stack;
}

static void
restore_gpm_state (int fd)
{
  gpm_tried = gpm_state_information[fd].gpm_tried;
  gpm_flag = gpm_state_information[fd].gpm_flag;
  gpm_stack = gpm_state_information[fd].gpm_stack;
  gpm_consolefd = gpm_fd = fd;
}

static void
clear_gpm_state (int fd)
{
  if (fd >= 0)
    memset (&gpm_state_information[fd], '\0', sizeof (struct __gpm_state));
  gpm_tried = gpm_flag = 1;
  gpm_fd = gpm_consolefd = -1;
  gpm_stack = NULL;
}

static int
get_process_infd (Lisp_Process *p)
{
  Lisp_Object instr, outstr, errstr;
  get_process_streams (p, &instr, &outstr, &errstr);
  assert (!NILP (instr));
  return filedesc_stream_fd (XLSTREAM (instr));
}

DEFUN ("receive-gpm-event", Freceive_gpm_event, 0, 2, 0, /*
Run GPM_GetEvent().
This function is the process handler for the GPM connection.
*/
       (process, string))
{
  Gpm_Event ev;
  int modifiers = 0;
  int button = 1;
  Lisp_Object fake_event = Qnil;
  Lisp_Event *event = NULL;
  struct gcpro gcpro1;
  static int num_events;

  CHECK_PROCESS (process);

  restore_gpm_state (get_process_infd (XPROCESS (process)));

  if (!Gpm_GetEvent (&ev))
    {
      warn_when_safe (Qnil, Qerror,
		      "Gpm_GetEvent failed - %d", gpm_fd);
      return (Qzero);
    }

  GCPRO1 (fake_event);

  num_events++;

  fake_event = Fmake_event (Qnil, Qnil);
  event = XEVENT (fake_event);

  event->timestamp = 0;
  event->channel = Fselected_frame (Qnil); /* CONSOLE_SELECTED_FRAME (con); */

  /* Whow, wouldn't named defines be NICE!?!?! */
  modifiers = 0;

  if (ev.modifiers & 1)   modifiers |= XEMACS_MOD_SHIFT;
  if (ev.modifiers & 2)   modifiers |= XEMACS_MOD_META;
  if (ev.modifiers & 4)   modifiers |= XEMACS_MOD_CONTROL;
  if (ev.modifiers & 8)   modifiers |= XEMACS_MOD_META;

  if (ev.buttons & GPM_B_LEFT)
    button = 1;
  else if (ev.buttons & GPM_B_MIDDLE)
    button = 2;
  else if (ev.buttons & GPM_B_RIGHT)
    button = 3;

  switch (GPM_BARE_EVENTS (ev.type))
    {
    case GPM_DOWN:
    case GPM_UP:
      event->event_type =
	(ev.type & GPM_DOWN) ? button_press_event : button_release_event;
      event->event.button.x         = ev.x;
      event->event.button.y         = ev.y;
      event->event.button.button    = button;
      event->event.button.modifiers = modifiers;
      break;
    case GPM_MOVE:
    case GPM_DRAG:
      event->event_type             = pointer_motion_event;
      event->event.motion.x         = ev.x;
      event->event.motion.y         = ev.y;
      event->event.motion.modifiers = modifiers;
    default:
      /* This will never happen */
      break;
    }

  /* Handle the event */
  enqueue_event (fake_event, &gpm_event_queue, &gpm_event_queue_tail);

  UNGCPRO;

  return (Qzero);
}

static void turn_off_gpm (char *process_name)
{
  Lisp_Object process = Fget_process (build_string (process_name));
  int fd = -1;

  if (NILP (process))
    /* Something happened to our GPM process - fail silently */
    return;

  fd = get_process_infd (XPROCESS (process));

  restore_gpm_state (fd);

  Gpm_Close();

  clear_gpm_state (fd);

  Fdelete_process (build_string (process_name));
}

#ifdef TIOCLINUX
static Lisp_Object
tty_get_foreign_selection (Lisp_Object selection_symbol,
			   Lisp_Object target_type)
{
  /* This function can GC */
  struct device *d = decode_device (Qnil);
  int fd = DEVICE_INFD (d);
  char c = 3;
  Lisp_Object output_stream = Qnil;
  Lisp_Object terminal_stream = Qnil;
  Lisp_Object output_string = Qnil;
  struct gcpro gcpro1,gcpro2,gcpro3;

  GCPRO3(output_stream,terminal_stream,output_string);

  /* The ioctl() to paste actually puts things in the input queue of
   ** the virtual console, so we need to trap that data, since we are
   ** supposed to return the actual string selection from this
   ** function.
   */

  /* I really hate doing this, but it doesn't seem to cause any
   ** problems, and it makes the Lstream_read stuff further down
   ** error out correctly instead of trying to indefinitely read from
   ** the console.
   **
   ** There is no set_descriptor_blocking() function call, but in my
   ** testing under linux, it has not proved fatal to leave the
   ** descriptor in non-blocking mode.
   **
   ** William Perry Nov 5, 1999
   */
  set_descriptor_non_blocking (fd);

  /* We need two streams, one for reading from the selected device,
   ** and one to write the data into.  There is no writable version
   ** of the lisp-string lstream, so we make do with a resizing
   ** buffer stream, and make a string out of it after we are
   ** done.
   */
  output_stream = make_resizing_buffer_output_stream ();
  terminal_stream = make_filedesc_input_stream (fd, 0, -1, LSTR_BLOCKED_OK);
  output_string = Qnil;

  /* #### We should arguably use a specbind() and an unwind routine here,
   ** #### but I don't care that much right now.
   */
  if (NILP (output_stream) || NILP (terminal_stream))
    /* Should we signal an error here? */
    goto out;

  if (ioctl (fd, TIOCLINUX, &c) < 0)
    {
      /* Could not get the selection - eek */
      UNGCPRO;
      return (Qnil);
    }

  while (1)
    {
      Intbyte tempbuf[1024];	/* some random amount */
      Bytecount i;
      Bytecount size_in_bytes =
	Lstream_read (XLSTREAM (terminal_stream),
		      tempbuf, sizeof (tempbuf));

      if (size_in_bytes <= 0)
	/* end of the stream */
	break;

      /* convert CR->LF */
      for (i = 0; i < size_in_bytes; i++)
	{
	  if (tempbuf[i] == '\r')
	    tempbuf[i] = '\n';
	}

      Lstream_write (XLSTREAM (output_stream), tempbuf, size_in_bytes);
    }

  Lstream_flush (XLSTREAM (output_stream));

  output_string =
    make_string (resizing_buffer_stream_ptr (XLSTREAM (output_stream)),
		 Lstream_byte_count (XLSTREAM (output_stream)));

  Lstream_delete (XLSTREAM (output_stream));
  Lstream_delete (XLSTREAM (terminal_stream));

 out:
  UNGCPRO;
  return (output_string);
}

static Lisp_Object
tty_selection_exists_p (Lisp_Object selection, Lisp_Object selection_type)
{
  return (Qt);
}
#endif /* TIOCLINUX */

#if 0
static Lisp_Object
tty_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
		   Lisp_Object how_to_add, Lisp_Object selection_type)
{
  /* There is no way to do this cleanly - the GPM selection
   ** 'protocol' (actually the TIOCLINUX ioctl) requires a start and
   ** end position on the _screen_, not a string to stick in there.
   ** Lame.
   **
   ** William Perry Nov 4, 1999
   */
}
#endif

/* This function appears to work once in a blue moon.  I'm not sure
 ** exactly why either.  *sigh*
 **
 ** William Perry Nov 4, 1999
 **
 ** Apparently, this is the way (mouse-position) is supposed to work,
 ** and I was just expecting something else.  (mouse-pixel-position)
 ** works just fine.
 **
 ** William Perry Nov 7, 1999
 */
static int
tty_get_mouse_position (struct device *d, Lisp_Object *frame, int *x, int *y)
{
  Gpm_Event ev;
  int num_buttons;

  memset(&ev,'\0',sizeof(ev));

  num_buttons = Gpm_GetSnapshot(&ev);

  if (!num_buttons)
    /* This means there are events pending... */

    /* #### In theory, we should drain the events pending, stick
     ** #### them in the queue, and return the mouse position
     ** #### anyway.
     */
    return (-1);
  *x = ev.x;
  *y = ev.y;
  *frame = DEVICE_SELECTED_FRAME (d);
  return (1);
}

static void
tty_set_mouse_position (struct window *w, int x, int y)
{
  /*
     #### I couldn't find any GPM functions that set the mouse position.
     #### Mr. Perry had left this function empty; that must be why.
     #### karlheg
     */
}

static int gpm_event_pending_p (int user_p)
{
  Lisp_Object event;

  EVENT_CHAIN_LOOP (event, gpm_event_queue)
    {
      if (!user_p || command_event_p (event))
	return (1);
    }
  return (orig_event_pending_p (user_p));
}

static void gpm_next_event_cb (Lisp_Event *event)
{
  /* #### It would be nice to preserve some sort of ordering of the
   ** #### different types of events, but that would be quite a bit
   ** #### of work, and would more than likely break the abstraction
   ** #### between the other event loops and this one.
   */

  if (!NILP (gpm_event_queue))
    {
      Lisp_Object queued_event =
	dequeue_event (&gpm_event_queue, &gpm_event_queue_tail);
      *event = *(XEVENT (queued_event));

      if (event->event_type == pointer_motion_event)
	{
	  struct device *d = decode_device (event->channel);
	  int fd = DEVICE_INFD (d);

	  /* Ok, now this is just freaky.  Bear with me though.
	   **
	   ** If you run gnuclient and attach to a XEmacs running in
	   ** X or on another TTY, the mouse cursor does not get
	   ** drawn correctly.  This is because the ioctl() fails
	   ** with EPERM because the TTY specified is not our
	   ** controlling terminal.  If you are the superuser, it
	   ** will work just spiffy.  The appropriate source file (at
	   ** least in linux 2.2.x) is
	   ** .../linux/drivers/char/console.c in the function
	   ** tioclinux().  The following bit of code is brutal to
	   ** us:
	   **
	   ** if (current->tty != tty && !suser())
	   **    return -EPERM;
	   **
	   ** I even tried setting us as a process leader, removing
	   ** our controlling terminal, and then using the TIOCSCTTY
	   ** to set up a new controlling terminal, all with no luck.
	   **
	   ** What is even weirder is if you run XEmacs in a VC, and
	   ** attach to it from another VC with gnuclient, go back to
	   ** the original VC and hit a key, the mouse pointer
	   ** displays (in BOTH VCs), until you hit a key in the
	   ** second VC, after which it does not display in EITHER
	   ** VC.  Bizarre, no?
	   **
	   ** All I can say is thank god Linux comes with source code
	   ** or I would have been completely confused.  Well, ok,
	   ** I'm still completely confused.  I don't see why they
	   ** don't just check the permissions on the device
	   ** (actually, if you have enough access to it to get the
	   ** console's file descriptor, you should be able to do
	   ** with it as you wish, but maybe that is just me).
	   **
	   ** William M. Perry - Nov 9, 1999
	   */

	  Gpm_DrawPointer (event->event.motion.x,event->event.motion.y, fd);
	}

      return;
    }

  orig_next_event_cb (event);
}

static void hook_event_callbacks_once (void)
{
  static int hooker;

  if (!hooker)
    {
      orig_event_pending_p = event_stream->event_pending_p;
      orig_next_event_cb = event_stream->next_event_cb;
      event_stream->event_pending_p = gpm_event_pending_p;
      event_stream->next_event_cb = gpm_next_event_cb;
      hooker = 1;
    }
}

static void hook_console_methods_once (void)
{
  static int hooker;

  if (!hooker)
    {
      /* Install the mouse position methods for the TTY console type */
      CONSOLE_HAS_METHOD (tty, get_mouse_position);
      CONSOLE_HAS_METHOD (tty, set_mouse_position);
      CONSOLE_HAS_METHOD (tty, get_foreign_selection);
      CONSOLE_HAS_METHOD (tty, selection_exists_p);
#if 0
      CONSOLE_HAS_METHOD (tty, own_selection);
#endif
    }
}

DEFUN ("gpm-enabled-p", Fgpm_enabled_p, 0, 1, 0, /*
Return non-nil if GPM mouse support is currently enabled on DEVICE.
*/
       (device))
{
  char *console_name = ttyname (DEVICE_INFD (decode_device (device)));
  char process_name[1024];
  Lisp_Object proc;

  if (!console_name)
    return (Qnil);

  memset (process_name, '\0', sizeof(process_name));
  snprintf (process_name, sizeof(process_name) - 1, "gpm for %s",
	    console_name);

  proc = Fget_process (build_string (process_name));

  if (NILP (proc))
    return (Qnil);

  if (1)			/* (PROCESS_LIVE_P (proc)) */
    return (Qt);
  return (Qnil);
}

DEFUN ("gpm-enable", Fgpm_enable, 0, 2, 0, /*
Toggle accepting of GPM mouse events.
*/
       (device, arg))
{
  Gpm_Connect conn;
  int rval;
  Lisp_Object gpm_process;
  Lisp_Object gpm_filter;
  struct device *d = decode_device (device);
  int fd = DEVICE_INFD (d);
  char *console_name = ttyname (fd);
  char process_name[1024];

  hook_event_callbacks_once ();
  hook_console_methods_once ();

  if (noninteractive)
    invalid_operation ("Can't connect to GPM in batch mode", Qunbound);

  if (!console_name)
    /* Something seriously wrong here... */
    return (Qnil);

  memset (process_name, '\0', sizeof(process_name));
  snprintf (process_name, sizeof(process_name) - 1, "gpm for %s",
	    console_name);

  if (NILP (arg))
    {
      turn_off_gpm (process_name);
      return (Qnil);
    }

  /* DANGER DANGER.
   ** Though shalt not call (gpm-enable t) after we have already
   ** started, or stuff blows up.
   */
  if (!NILP (Fgpm_enabled_p (device)))
    invalid_operation ("GPM already enabled for this console", Qunbound);

  conn.eventMask = GPM_DOWN|GPM_UP|GPM_MOVE|GPM_DRAG;
  conn.defaultMask = GPM_MOVE;
  conn.minMod = 0;
  conn.maxMod = ((1 << KG_SHIFT) | (1 << KG_ALT) | (1 << KG_CTRL));

  /* Reset some silly static variables so that multiple Gpm_Open()
   ** calls have even a slight chance of working
   */
  gpm_tried = 0;
  gpm_flag = 0;
  gpm_stack = NULL;

  /* Make sure Gpm_Open() does ioctl() on the correct
   ** descriptor, or it can get the wrong terminal sizes, etc.
   */
  gpm_consolefd = fd;

  /* We have to pass the virtual console manually, otherwise if you
   ** use 'gnuclient -nw' to connect to an XEmacs that is running in
   ** X, Gpm_Open() tries to use ttyname(0 | 1 | 2) to find out which
   ** console you are using, which is of course not correct for the
   ** new tty device.
   */
  if (strncmp (console_name, "/dev/tty", 8) || !isdigit (console_name[8]))
    /* Urk, something really wrong */
    return (Qnil);

  rval = Gpm_Open (&conn, atoi (console_name + 8));

  switch (rval)
    {
    case -1:			/* General failure */
      break;
    case -2:			/* We are running under an XTerm */
      Gpm_Close();
      break;
    default:
      /* Is this really necessary? */
      set_descriptor_non_blocking (gpm_fd);
      store_gpm_state (gpm_fd);
      gpm_process =
	connect_to_file_descriptor (build_string (process_name), Qnil,
				    make_int (gpm_fd),
				    make_int (gpm_fd));

      if (!NILP (gpm_process))
	{
	  rval = 0;
	  Fprocess_kill_without_query (gpm_process, Qnil);
	  gpm_filter = wrap_subr (&SFreceive_gpm_event);
	  set_process_filter (gpm_process, gpm_filter, 1, 0);

	  /* Keep track of the device for later */
	  /* Fput (gpm_process, intern ("gpm-device"), device); */
	}
      else
	{
	  Gpm_Close ();
	  rval = -1;
	}
    }

  return (rval ? Qnil : Qt);
}

void vars_of_gpmevent (void)
{
  gpm_event_queue = Qnil;
  gpm_event_queue_tail = Qnil;
  staticpro (&gpm_event_queue);
  staticpro (&gpm_event_queue_tail);
  dump_add_root_object (&gpm_event_queue);
  dump_add_root_object (&gpm_event_queue_tail);
}

void syms_of_gpmevent (void)
{
  DEFSUBR (Freceive_gpm_event);
  DEFSUBR (Fgpm_enable);
  DEFSUBR (Fgpm_enabled_p);
}

#endif /* HAVE_GPM */