Mercurial > hg > xemacs-beta
view src/gpmevent.c @ 2570:269c376d86ef
[xemacs-hg @ 2005-02-06 22:51:07 by viteno]
Update xemacs_extra_name.
author | viteno |
---|---|
date | Sun, 06 Feb 2005 22:51:07 +0000 |
parents | 04bc9d2f42c7 |
children | 6fa9919a9a0b |
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 "frame.h" #include "device-impl.h" #include "console-impl.h" #include "console-tty-impl.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, UNUSED (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: SET_EVENT_TYPE (event, (ev.type & GPM_DOWN) ? button_press_event : button_release_event); SET_EVENT_BUTTON_X (event, ev.x); SET_EVENT_BUTTON_Y (event, ev.y); SET_EVENT_BUTTON_BUTTON (event, button); SET_EVENT_BUTTON_MODIFIERS (event, modifiers); break; case GPM_MOVE: case GPM_DRAG: SET_EVENT_TYPE (event, pointer_motion_event); SET_EVENT_MOTION_X (event, ev.x); SET_EVENT_MOTION_Y (event, ev.y); SET_EVENT_MOTION_MODIFIERS (event, 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 UNUSED (selection_symbol), Lisp_Object UNUSED (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) { Ibyte 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 UNUSED (selection), Lisp_Object UNUSED (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 *UNUSED (w), int UNUSED (x), int UNUSED (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_MOTION_X (event),EVENT_MOTION_Y (event), 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_lisp_object (&gpm_event_queue); dump_add_root_lisp_object (&gpm_event_queue_tail); } void syms_of_gpmevent (void) { DEFSUBR (Freceive_gpm_event); DEFSUBR (Fgpm_enable); DEFSUBR (Fgpm_enabled_p); } #endif /* HAVE_GPM */