Reformatted.
[chise/xemacs-chise.git] / src / gpmevent.c
1 /* GPM (General purpose mouse) functions
2    Copyright (C) 1997 William M. Perry <wmperry@gnu.org>
3    Copyright (C) 1999 Free Software Foundation, Inc.
4
5 This file is part of XEmacs.
6
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; see the file COPYING.  If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 /* Synched up with: Not in FSF. */
23
24 /* Authors: William Perry */
25
26 #include <config.h>
27 #include "lisp.h"
28 #include "console.h"
29 #include "console-tty.h"
30 #include "device.h"
31 #include "events.h"
32 #include "events-mod.h"
33 #include "sysdep.h"
34 #include "commands.h"
35 #include "lstream.h"
36 #include "sysproc.h" /* for MAXDESC */
37 #include "process.h"
38
39 #ifdef HAVE_GPM
40 #include "gpmevent.h"
41 #include <gpm.h>
42
43 #if (!defined(__linux__))       /* possible under xterm */
44 #define KG_SHIFT        0
45 #define KG_CTRL         2
46 #define KG_ALT          3
47 #else
48 #include <linux/keyboard.h>
49 #endif
50
51 extern int gpm_tried;
52 extern void *gpm_stack;
53
54 static int (*orig_event_pending_p) (int);
55 static void (*orig_next_event_cb) (Lisp_Event *);
56
57 static Lisp_Object gpm_event_queue;
58 static Lisp_Object gpm_event_queue_tail;
59
60 struct __gpm_state {
61         int gpm_tried;
62         int gpm_flag;
63         void *gpm_stack;
64 };
65
66 static struct __gpm_state gpm_state_information[MAXDESC];
67
68 static void
69 store_gpm_state (int fd)
70 {
71         gpm_state_information[fd].gpm_tried = gpm_tried;
72         gpm_state_information[fd].gpm_flag = gpm_flag;
73         gpm_state_information[fd].gpm_stack = gpm_stack;
74 }
75
76 static void
77 restore_gpm_state (int fd)
78 {
79         gpm_tried = gpm_state_information[fd].gpm_tried;
80         gpm_flag = gpm_state_information[fd].gpm_flag;
81         gpm_stack = gpm_state_information[fd].gpm_stack;
82         gpm_consolefd = gpm_fd = fd;
83 }
84
85 static void
86 clear_gpm_state (int fd)
87 {
88         if (fd >= 0)
89         {
90                 memset(&gpm_state_information[fd], '\0', sizeof(struct __gpm_state));
91         }
92         gpm_tried = gpm_flag = 1;
93         gpm_fd = gpm_consolefd = -1;
94         gpm_stack = NULL;
95 }
96
97 static int
98 get_process_infd (Lisp_Process *p)
99 {
100   Lisp_Object instr, outstr;
101   get_process_streams (p, &instr, &outstr);
102   assert (!NILP (instr));
103   return filedesc_stream_fd (XLSTREAM (instr));
104 }
105
106 DEFUN ("receive-gpm-event", Freceive_gpm_event, 0, 2, 0, /*
107 Run GPM_GetEvent().
108 This function is the process handler for the GPM connection.
109 */
110        (process, string))
111 {
112         Gpm_Event ev;
113         int modifiers = 0;
114         int button = 1;
115         Lisp_Object fake_event = Qnil;
116         Lisp_Event *event = NULL;
117         struct gcpro gcpro1;
118         static int num_events;
119
120         CHECK_PROCESS (process);
121
122         restore_gpm_state (get_process_infd (XPROCESS (process)));
123
124         if (!Gpm_GetEvent(&ev))
125         {
126                 warn_when_safe (Qnil, Qcritical, "Gpm_GetEvent failed - %d", gpm_fd);
127                 return(Qzero);
128         }
129
130         GCPRO1(fake_event);
131
132         num_events++;
133
134         fake_event = Fmake_event (Qnil, Qnil);
135         event = XEVENT(fake_event);
136
137         event->timestamp = 0;
138         event->channel   = Fselected_frame (Qnil); /* CONSOLE_SELECTED_FRAME (con); */
139
140         /* Whow, wouldn't named defines be NICE!?!?! */
141         modifiers = 0;
142
143         if (ev.modifiers & 1)   modifiers |= XEMACS_MOD_SHIFT;
144         if (ev.modifiers & 2)   modifiers |= XEMACS_MOD_META;
145         if (ev.modifiers & 4)   modifiers |= XEMACS_MOD_CONTROL;
146         if (ev.modifiers & 8)   modifiers |= XEMACS_MOD_META;
147
148         if (ev.buttons & GPM_B_LEFT)
149         {
150                 button = 1;
151         }
152         else if (ev.buttons & GPM_B_MIDDLE)
153         {
154                 button = 2;
155         }
156         else if (ev.buttons & GPM_B_RIGHT)
157         {
158                 button = 3;
159         }
160
161         switch (GPM_BARE_EVENTS(ev.type)) {
162         case GPM_DOWN:
163         case GPM_UP:
164                 event->event_type =
165                         (ev.type & GPM_DOWN) ? button_press_event : button_release_event;
166                 event->event.button.x         = ev.x;
167                 event->event.button.y         = ev.y;
168                 event->event.button.button    = button;
169                 event->event.button.modifiers = modifiers;
170                 break;
171         case GPM_MOVE:
172         case GPM_DRAG:
173                 event->event_type             = pointer_motion_event;
174                 event->event.motion.x         = ev.x;
175                 event->event.motion.y         = ev.y;
176                 event->event.motion.modifiers = modifiers;
177         default:
178                 /* This will never happen */
179                 break;
180         }
181
182         /* Handle the event */
183         enqueue_event (fake_event, &gpm_event_queue, &gpm_event_queue_tail);
184
185         UNGCPRO;
186
187         return (Qzero);
188 }
189
190 static void turn_off_gpm (char *process_name)
191 {
192         Lisp_Object process = Fget_process (build_string (process_name));
193         int fd = -1;
194
195         if (NILP (process))
196         {
197                 /* Something happened to our GPM process - fail silently */
198                 return;
199         }
200
201         fd = get_process_infd (XPROCESS (process));
202
203         restore_gpm_state (fd);
204
205         Gpm_Close();
206
207         clear_gpm_state (fd);
208
209         Fdelete_process (build_string (process_name));
210 }
211
212 #ifdef TIOCLINUX
213 static Lisp_Object
214 tty_get_foreign_selection (Lisp_Object selection_symbol, Lisp_Object target_type)
215 {
216         /* This function can GC */
217         struct device *d = decode_device (Qnil);
218         int fd = DEVICE_INFD (d);
219         char c = 3;
220         Lisp_Object output_stream = Qnil;
221         Lisp_Object terminal_stream = Qnil ;
222         Lisp_Object output_string = Qnil;
223         struct gcpro gcpro1,gcpro2,gcpro3;
224
225         GCPRO3(output_stream,terminal_stream,output_string);
226
227         /* The ioctl() to paste actually puts things in the input queue of
228         ** the virtual console, so we need to trap that data, since we are
229         ** supposed to return the actual string selection from this
230         ** function.
231         */
232
233         /* I really hate doing this, but it doesn't seem to cause any
234         ** problems, and it makes the Lstream_read stuff further down
235         ** error out correctly instead of trying to indefinitely read from
236         ** the console.
237         **
238         ** There is no set_descriptor_blocking() function call, but in my
239         ** testing under linux, it has not proved fatal to leave the
240         ** descriptor in non-blocking mode.
241         **
242         ** William Perry Nov 5, 1999
243         */
244         set_descriptor_non_blocking (fd);
245
246         /* We need two streams, one for reading from the selected device,
247         ** and one to write the data into.  There is no writable version
248         ** of the lisp-string lstream, so we make do with a resizing
249         ** buffer stream, and make a string out of it after we are
250         ** done.
251         */
252         output_stream = make_resizing_buffer_output_stream ();
253         terminal_stream = make_filedesc_input_stream (fd, 0, -1, LSTR_BLOCKED_OK);
254         output_string = Qnil;
255
256         /* #### We should arguably use a specbind() and an unwind routine here,
257         ** #### but I don't care that much right now.
258         */
259         if (NILP (output_stream) || NILP (terminal_stream))
260         {
261                 /* Should we signal an error here? */
262                 goto out;
263         }
264
265         if (ioctl (fd, TIOCLINUX, &c) < 0)
266         {
267                 /* Could not get the selection - eek */
268                 UNGCPRO;
269                 return (Qnil);
270         }
271
272         while (1)
273         {
274                 Bufbyte tempbuf[1024]; /* some random amount */
275                 Lstream_data_count i;
276                 Lstream_data_count size_in_bytes =
277                   Lstream_read (XLSTREAM (terminal_stream),
278                                 tempbuf, sizeof (tempbuf));
279
280                 if (size_in_bytes <= 0)
281                 {
282                         /* end of the stream */
283                         break;
284                 }
285
286                 /* convert CR->LF */
287                 for (i = 0; i < size_in_bytes; i++)
288                 {
289                         if (tempbuf[i] == '\r')
290                         {
291                                 tempbuf[i] = '\n';
292                         }
293                 }
294
295                 Lstream_write (XLSTREAM (output_stream), tempbuf, size_in_bytes);
296         }
297
298         Lstream_flush (XLSTREAM (output_stream));
299
300         output_string = make_string (resizing_buffer_stream_ptr (XLSTREAM (output_stream)),
301                                                                  Lstream_byte_count (XLSTREAM (output_stream)));
302
303         Lstream_delete (XLSTREAM (output_stream));
304         Lstream_delete (XLSTREAM (terminal_stream));
305
306  out:
307         UNGCPRO;
308         return (output_string);
309 }
310
311 static Lisp_Object
312 tty_selection_exists_p (Lisp_Object selection, Lisp_Object selection_type)
313 {
314         return (Qt);
315 }
316 #endif /* TIOCLINUX */
317
318 #if 0
319 static Lisp_Object
320 tty_own_selection (Lisp_Object selection_name, Lisp_Object selection_value,
321                    Lisp_Object how_to_add, Lisp_Object selection_type)
322 {
323         /* There is no way to do this cleanly - the GPM selection
324         ** 'protocol' (actually the TIOCLINUX ioctl) requires a start and
325         ** end position on the _screen_, not a string to stick in there.
326         ** Lame.
327         **
328         ** William Perry Nov 4, 1999
329         */
330 }
331 #endif
332
333 /* This function appears to work once in a blue moon.  I'm not sure
334 ** exactly why either.  *sigh*
335 **
336 ** William Perry Nov 4, 1999
337 **
338 ** Apparently, this is the way (mouse-position) is supposed to work,
339 ** and I was just expecting something else.  (mouse-pixel-position)
340 ** works just fine.
341 **
342 ** William Perry Nov 7, 1999
343 */
344 static int
345 tty_get_mouse_position (struct device *d, Lisp_Object *frame, int *x, int *y)
346 {
347         Gpm_Event ev;
348         int num_buttons;
349
350         memset(&ev,'\0',sizeof(ev));
351
352         num_buttons = Gpm_GetSnapshot(&ev);
353
354         if (!num_buttons)
355         {
356                 /* This means there are events pending... */
357
358                 /* #### In theory, we should drain the events pending, stick
359                 ** #### them in the queue, and return the mouse position
360                 ** #### anyway.
361                 */
362                 return(-1);
363         }
364         *x = ev.x;
365         *y = ev.y;
366         *frame = DEVICE_SELECTED_FRAME (d);
367         return (1);
368 }
369
370 static void
371 tty_set_mouse_position (struct window *w, int x, int y)
372 {
373         /*
374            #### I couldn't find any GPM functions that set the mouse position.
375            #### Mr. Perry had left this function empty; that must be why.
376            #### karlheg
377         */
378 }
379
380 static int gpm_event_pending_p (int user_p)
381 {
382         Lisp_Object event;
383
384         EVENT_CHAIN_LOOP (event, gpm_event_queue)
385         {
386                 if (!user_p || command_event_p (event))
387                 {
388                         return (1);
389                 }
390         }
391         return (orig_event_pending_p (user_p));
392 }
393
394 static void gpm_next_event_cb (Lisp_Event *event)
395 {
396         /* #### It would be nice to preserve some sort of ordering of the
397         ** #### different types of events, but that would be quite a bit
398         ** #### of work, and would more than likely break the abstraction
399         ** #### between the other event loops and this one.
400         */
401
402         if (!NILP (gpm_event_queue))
403         {
404                 Lisp_Object queued_event = dequeue_event (&gpm_event_queue, &gpm_event_queue_tail);
405                 *event = *(XEVENT (queued_event));
406
407                 if (event->event_type == pointer_motion_event)
408                 {
409                         struct device *d = decode_device (event->channel);
410                         int fd = DEVICE_INFD (d);
411
412                         /* Ok, now this is just freaky.  Bear with me though.
413                         **
414                         ** If you run gnuclient and attach to a XEmacs running in
415                         ** X or on another TTY, the mouse cursor does not get
416                         ** drawn correctly.  This is because the ioctl() fails
417                         ** with EPERM because the TTY specified is not our
418                         ** controlling terminal.  If you are the superuser, it
419                         ** will work just spiffy.  The appropriate source file (at
420                         ** least in linux 2.2.x) is
421                         ** .../linux/drivers/char/console.c in the function
422                         ** tioclinux().  The following bit of code is brutal to
423                         ** us:
424                         **
425                         ** if (current->tty != tty && !suser())
426                         **    return -EPERM;
427                         **
428                         ** I even tried setting us as a process leader, removing
429                         ** our controlling terminal, and then using the TIOCSCTTY
430                         ** to set up a new controlling terminal, all with no luck.
431                         **
432                         ** What is even weirder is if you run XEmacs in a VC, and
433                         ** attach to it from another VC with gnuclient, go back to
434                         ** the original VC and hit a key, the mouse pointer
435                         ** displays (in BOTH VCs), until you hit a key in the
436                         ** second VC, after which it does not display in EITHER
437                         ** VC.  Bizarre, no?
438                         **
439                         ** All I can say is thank god Linux comes with source code
440                         ** or I would have been completely confused.  Well, ok,
441                         ** I'm still completely confused.  I don't see why they
442                         ** don't just check the permissions on the device
443                         ** (actually, if you have enough access to it to get the
444                         ** console's file descriptor, you should be able to do
445                         ** with it as you wish, but maybe that is just me).
446                         **
447                         ** William M. Perry - Nov 9, 1999
448                         */
449
450                         Gpm_DrawPointer (event->event.motion.x,event->event.motion.y, fd);
451                 }
452
453                 return;
454         }
455
456         orig_next_event_cb (event);
457 }
458
459 static void hook_event_callbacks_once (void)
460 {
461         static int hooker;
462
463         if (!hooker)
464         {
465                 orig_event_pending_p = event_stream->event_pending_p;
466                 orig_next_event_cb = event_stream->next_event_cb;
467                 event_stream->event_pending_p = gpm_event_pending_p;
468                 event_stream->next_event_cb = gpm_next_event_cb;
469                 hooker = 1;
470         }
471 }
472
473 static void hook_console_methods_once (void)
474 {
475         static int hooker;
476
477         if (!hooker)
478         {
479                 /* Install the mouse position methods for the TTY console type */
480                 CONSOLE_HAS_METHOD (tty, get_mouse_position);
481                 CONSOLE_HAS_METHOD (tty, set_mouse_position);
482                 CONSOLE_HAS_METHOD (tty, get_foreign_selection);
483                 CONSOLE_HAS_METHOD (tty, selection_exists_p);
484 #if 0
485                 CONSOLE_HAS_METHOD (tty, own_selection);
486 #endif
487         }
488 }
489
490 DEFUN ("gpm-enabled-p", Fgpm_enabled_p, 0, 1, 0, /*
491 Return non-nil if GPM mouse support is currently enabled on DEVICE.
492 */
493            (device))
494 {
495         char *console_name = ttyname (DEVICE_INFD (decode_device (device)));
496         char process_name[1024];
497         Lisp_Object proc;
498
499         if (!console_name)
500         {
501                 return (Qnil);
502         }
503
504         memset (process_name, '\0', sizeof(process_name));
505         snprintf (process_name, sizeof(process_name) - 1, "gpm for %s", console_name);
506
507         proc = Fget_process (build_string (process_name));
508
509         if (NILP (proc))
510         {
511                 return (Qnil);
512         }
513
514         if (1) /* (PROCESS_LIVE_P (proc)) */
515         {
516                 return (Qt);
517         }
518         return (Qnil);
519 }
520
521 DEFUN ("gpm-enable", Fgpm_enable, 0, 2, 0, /*
522 Toggle accepting of GPM mouse events.
523 */
524            (device, arg))
525 {
526         Gpm_Connect conn;
527         int rval;
528         Lisp_Object gpm_process;
529         Lisp_Object gpm_filter;
530         struct device *d = decode_device (device);
531         int fd = DEVICE_INFD (d);
532         char *console_name = ttyname (fd);
533         char process_name[1024];
534
535         hook_event_callbacks_once ();
536         hook_console_methods_once ();
537
538         if (noninteractive)
539         {
540                 error ("Can't connect to GPM in batch mode.");
541         }
542
543         if (!console_name)
544         {
545                 /* Something seriously wrong here... */
546                 return (Qnil);
547         }
548
549         memset (process_name, '\0', sizeof(process_name));
550         snprintf (process_name, sizeof(process_name) - 1, "gpm for %s", console_name);
551
552         if (NILP (arg))
553         {
554                 turn_off_gpm (process_name);
555                 return (Qnil);
556         }
557
558         /* DANGER DANGER.
559         ** Though shalt not call (gpm-enable t) after we have already
560         ** started, or stuff blows up.
561         */
562         if (!NILP (Fgpm_enabled_p (device)))
563         {
564                 error ("GPM already enabled for this console.");
565         }
566
567         conn.eventMask = GPM_DOWN|GPM_UP|GPM_MOVE|GPM_DRAG;
568         conn.defaultMask = GPM_MOVE;
569         conn.minMod = 0;
570         conn.maxMod = ((1<<KG_SHIFT)|(1<<KG_ALT)|(1<<KG_CTRL));
571
572         /* Reset some silly static variables so that multiple Gpm_Open()
573         ** calls have even a slight chance of working
574         */
575         gpm_tried = 0;
576         gpm_flag = 0;
577         gpm_stack = NULL;
578
579         /* Make sure Gpm_Open() does ioctl() on the correct
580         ** descriptor, or it can get the wrong terminal sizes, etc.
581         */
582         gpm_consolefd = fd;
583
584         /* We have to pass the virtual console manually, otherwise if you
585         ** use 'gnuclient -nw' to connect to an XEmacs that is running in
586         ** X, Gpm_Open() tries to use ttyname(0 | 1 | 2) to find out which
587         ** console you are using, which is of course not correct for the
588         ** new tty device.
589         */
590         if (strncmp (console_name, "/dev/tty",8) || !isdigit (console_name[8]))
591         {
592                 /* Urk, something really wrong */
593                 return (Qnil);
594         }
595
596         rval = Gpm_Open (&conn, atoi(console_name + 8));
597
598         switch (rval) {
599         case -1: /* General failure */
600                 break;
601         case -2: /* We are running under an XTerm */
602                 Gpm_Close();
603                 break;
604         default:
605                 /* Is this really necessary? */
606                 set_descriptor_non_blocking (gpm_fd);
607                 store_gpm_state (gpm_fd);
608                 gpm_process = connect_to_file_descriptor (build_string (process_name), Qnil,
609                                                                                                   make_int (gpm_fd),
610                                                                                                   make_int (gpm_fd));
611
612                 if (!NILP (gpm_process))
613                 {
614                         rval = 0;
615                         Fprocess_kill_without_query (gpm_process, Qnil);
616                         XSETSUBR (gpm_filter, &SFreceive_gpm_event);
617                         set_process_filter (gpm_process, gpm_filter, 1);
618
619                         /* Keep track of the device for later */
620                         /* Fput (gpm_process, intern ("gpm-device"), device); */
621                 }
622                 else
623                 {
624                         Gpm_Close();
625                         rval = -1;
626                 }
627         }
628
629         return(rval ? Qnil : Qt);
630 }
631
632 void vars_of_gpmevent (void)
633 {
634         gpm_event_queue = Qnil;
635         gpm_event_queue_tail = Qnil;
636         staticpro (&gpm_event_queue);
637         staticpro (&gpm_event_queue_tail);
638         dump_add_root_object (&gpm_event_queue);
639         dump_add_root_object (&gpm_event_queue_tail);
640 }
641
642 void syms_of_gpmevent (void)
643 {
644         DEFSUBR (Freceive_gpm_event);
645         DEFSUBR (Fgpm_enable);
646         DEFSUBR (Fgpm_enabled_p);
647 }
648
649 #endif /* HAVE_GPM */