X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Fgpmevent.c;h=d42c9ae20d97374302afb8f39026280fb8def01c;hb=c1fa38c050ef3bd87e784dea66eba3cdac585536;hp=77f373852366fbcf6b0621a422858ee448b46bf0;hpb=6883ee56ec887c2c48abe5b06b5e66aa74031910;p=chise%2Fxemacs-chise.git.1 diff --git a/src/gpmevent.c b/src/gpmevent.c index 77f3738..d42c9ae 100644 --- a/src/gpmevent.c +++ b/src/gpmevent.c @@ -1,4 +1,27 @@ -/* William Perry 1997 */ +/* GPM (General purpose mouse) functions + Copyright (C) 1997 William M. Perry + Copyright (C) 1999 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, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* Authors: William Perry */ #include #include "lisp.h" @@ -8,6 +31,10 @@ #include "events.h" #include "events-mod.h" #include "sysdep.h" +#include "commands.h" +#include "lstream.h" +#include "sysproc.h" /* for MAXDESC */ +#include "process.h" #ifdef HAVE_GPM #include "gpmevent.h" @@ -21,94 +48,602 @@ #include #endif -int -handle_gpm_read (struct Lisp_Event *event, struct console *con, int fd) -{ - Gpm_Event ev; - int modifiers = 0; - int type = -1; - int button = 1; - - if (!Gpm_GetEvent(&ev)) - return 0; - - event->timestamp = 0; - event->channel = CONSOLE_SELECTED_FRAME (con); - - /* Whow, wouldn't named defines be NICE!?!?! */ - modifiers = 0; - - if (ev.modifiers & 1) modifiers |= MOD_SHIFT; - if (ev.modifiers & 2) modifiers |= MOD_META; - if (ev.modifiers & 4) modifiers |= MOD_CONTROL; - if (ev.modifiers & 8) modifiers |= MOD_META; - - if (ev.type & GPM_DOWN) - type = GPM_DOWN; - else if (ev.type & GPM_UP) - type = GPM_UP; - else if (ev.type & GPM_MOVE) { - type = GPM_MOVE; - GPM_DRAWPOINTER(&ev); - } - - 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 (type) { - case GPM_DOWN: - case GPM_UP: - event->event_type = - 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: - event->event_type = pointer_motion_event; - event->event.motion.x = ev.x; - event->event.motion.y = ev.y; - event->event.motion.modifiers = modifiers; - default: - return 0; - } - return 1; -} - -void -connect_to_gpm (struct console *con) -{ - /* Only do this if we are running after dumping and really interactive */ - if (!noninteractive && initialized) { - /* We really only want to do this on a TTY */ - CONSOLE_TTY_MOUSE_FD (con) = -1; - if (EQ (CONSOLE_TYPE (con), Qtty)) { - Gpm_Connect conn; - int rval; - - conn.eventMask = GPM_DOWN|GPM_UP|GPM_MOVE; - conn.defaultMask = GPM_MOVE; - conn.minMod = 0; - conn.maxMod = ((1<= 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; + get_process_streams (p, &instr, &outstr); + 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, Qcritical, "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(); - break; - default: - set_descriptor_non_blocking (gpm_fd); - CONSOLE_TTY_MOUSE_FD (con) = gpm_fd; - } - } - } + + 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) + { + Bufbyte tempbuf[1024]; /* some random amount */ + Lstream_data_count i; + Lstream_data_count 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) + { + error ("Can't connect to GPM in batch mode."); + } + + 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))) + { + error ("GPM already enabled for this console."); + } + + conn.eventMask = GPM_DOWN|GPM_UP|GPM_MOVE|GPM_DRAG; + conn.defaultMask = GPM_MOVE; + conn.minMod = 0; + conn.maxMod = ((1<