XEmacs 21.2.28 "Hermes".
[chise/xemacs-chise.git.1] / 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;
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 |= MOD_SHIFT;
144         if (ev.modifiers & 2)   modifiers |= MOD_META;
145         if (ev.modifiers & 4)   modifiers |= MOD_CONTROL;
146         if (ev.modifiers & 8)   modifiers |= 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;
221         Lisp_Object terminal_stream;
222         Lisp_Object output_string;
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                 ssize_t i;
276                 ssize_t 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)
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 {
322         /* There is no way to do this cleanly - the GPM selection
323         ** 'protocol' (actually the TIOCLINUX ioctl) requires a start and
324         ** end position on the _screen_, not a string to stick in there.
325         ** Lame.
326         **
327         ** William Perry Nov 4, 1999
328         */
329 }
330 #endif
331
332 /* This function appears to work once in a blue moon.  I'm not sure
333 ** exactly why either.  *sigh*
334 **
335 ** William Perry Nov 4, 1999
336 **
337 ** Apparently, this is the way (mouse-position) is supposed to work,
338 ** and I was just expecting something else.  (mouse-pixel-position)
339 ** works just fine.
340 **
341 ** William Perry Nov 7, 1999
342 */
343 static int
344 tty_get_mouse_position (struct device *d, Lisp_Object *frame, int *x, int *y)
345 {
346         Gpm_Event ev;
347         int num_buttons;
348
349         memset(&ev,'\0',sizeof(ev));
350
351         num_buttons = Gpm_GetSnapshot(&ev);
352
353         if (!num_buttons)
354         {
355                 /* This means there are events pending... */
356
357                 /* #### In theory, we should drain the events pending, stick
358                 ** #### them in the queue, and return the mouse position
359                 ** #### anyway.
360                 */
361                 return(-1);
362         }
363         *x = ev.x;
364         *y = ev.y;
365         *frame = DEVICE_SELECTED_FRAME (d);
366         return (1);
367 }
368
369 static void
370 tty_set_mouse_position (struct window *w, int x, int y)
371 {
372         /*
373            #### I couldn't find any GPM functions that set the mouse position.
374            #### Mr. Perry had left this function empty; that must be why.
375            #### karlheg
376         */
377 }
378
379 static int gpm_event_pending_p (int user_p)
380 {
381         Lisp_Object event;
382
383         EVENT_CHAIN_LOOP (event, gpm_event_queue)
384         {
385                 if (!user_p || command_event_p (event))
386                 {
387                         return (1);
388                 }
389         }
390         return (orig_event_pending_p (user_p));
391 }
392
393 static void gpm_next_event_cb (Lisp_Event *event)
394 {
395         /* #### It would be nice to preserve some sort of ordering of the
396         ** #### different types of events, but that would be quite a bit
397         ** #### of work, and would more than likely break the abstraction
398         ** #### between the other event loops and this one.
399         */
400
401         if (!NILP (gpm_event_queue))
402         {
403                 Lisp_Object queued_event = dequeue_event (&gpm_event_queue, &gpm_event_queue_tail);
404                 *event = *(XEVENT (queued_event));
405
406                 if (event->event_type == pointer_motion_event)
407                 {
408                         struct device *d = decode_device (event->channel);
409                         int fd = DEVICE_INFD (d);
410
411                         /* Ok, now this is just freaky.  Bear with me though.
412                         **
413                         ** If you run gnuclient and attach to a XEmacs running in
414                         ** X or on another TTY, the mouse cursor does not get
415                         ** drawn correctly.  This is because the ioctl() fails
416                         ** with EPERM because the TTY specified is not our
417                         ** controlling terminal.  If you are the superuser, it
418                         ** will work just spiffy.  The appropriate source file (at
419                         ** least in linux 2.2.x) is
420                         ** .../linux/drivers/char/console.c in the function
421                         ** tioclinux().  The following bit of code is brutal to
422                         ** us:
423                         **
424                         ** if (current->tty != tty && !suser())
425                         **    return -EPERM;
426                         **
427                         ** I even tried setting us as a process leader, removing
428                         ** our controlling terminal, and then using the TIOCSCTTY
429                         ** to set up a new controlling terminal, all with no luck.
430                         **
431                         ** What is even weirder is if you run XEmacs in a VC, and
432                         ** attach to it from another VC with gnuclient, go back to
433                         ** the original VC and hit a key, the mouse pointer
434                         ** displays (in BOTH VCs), until you hit a key in the
435                         ** second VC, after which it does not display in EITHER
436                         ** VC.  Bizarre, no?
437                         **
438                         ** All I can say is thank god Linux comes with source code
439                         ** or I would have been completely confused.  Well, ok,
440                         ** I'm still completely confused.  I don't see why they
441                         ** don't just check the permissions on the device
442                         ** (actually, if you have enough access to it to get the
443                         ** console's file descriptor, you should be able to do
444                         ** with it as you wish, but maybe that is just me).
445                         **
446                         ** William M. Perry - Nov 9, 1999
447                         */
448
449                         Gpm_DrawPointer (event->event.motion.x,event->event.motion.y, fd);
450                 }
451
452                 return;
453         }
454
455         orig_next_event_cb (event);
456 }
457
458 static void hook_event_callbacks_once (void)
459 {
460         static int hooker;
461
462         if (!hooker)
463         {
464                 orig_event_pending_p = event_stream->event_pending_p;
465                 orig_next_event_cb = event_stream->next_event_cb;
466                 event_stream->event_pending_p = gpm_event_pending_p;
467                 event_stream->next_event_cb = gpm_next_event_cb;
468                 hooker = 1;
469         }
470 }
471
472 static void hook_console_methods_once (void)
473 {
474         static int hooker;
475
476         if (!hooker)
477         {
478                 /* Install the mouse position methods for the TTY console type */
479                 CONSOLE_HAS_METHOD (tty, get_mouse_position);
480                 CONSOLE_HAS_METHOD (tty, set_mouse_position);
481                 CONSOLE_HAS_METHOD (tty, get_foreign_selection);
482                 CONSOLE_HAS_METHOD (tty, selection_exists_p);
483 #if 0
484                 CONSOLE_HAS_METHOD (tty, own_selection);
485 #endif
486         }
487 }
488
489 DEFUN ("gpm-enabled-p", Fgpm_enabled_p, 0, 1, 0, /*
490 Return non-nil if GPM mouse support is currently enabled on DEVICE.
491 */
492            (device))
493 {
494         char *console_name = ttyname (DEVICE_INFD (decode_device (device)));
495         char process_name[1024];
496         Lisp_Object proc;
497
498         if (!console_name)
499         {
500                 return (Qnil);
501         }
502
503         memset (process_name, '\0', sizeof(process_name));
504         snprintf (process_name, sizeof(process_name) - 1, "gpm for %s", console_name);
505
506         proc = Fget_process (build_string (process_name));
507
508         if (NILP (proc))
509         {
510                 return (Qnil);
511         }
512
513         if (1) /* (PROCESS_LIVE_P (proc)) */
514         {
515                 return (Qt);
516         }
517         return (Qnil);
518 }
519
520 DEFUN ("gpm-enable", Fgpm_enable, 0, 2, 0, /*
521 Toggle accepting of GPM mouse events.
522 */
523            (device, arg))
524 {
525         Gpm_Connect conn;
526         int rval;
527         Lisp_Object gpm_process;
528         Lisp_Object gpm_filter;
529         struct device *d = decode_device (device);
530         int fd = DEVICE_INFD (d);
531         char *console_name = ttyname (fd);
532         char process_name[1024];
533
534         hook_event_callbacks_once ();
535         hook_console_methods_once ();
536
537         if (noninteractive)
538         {
539                 error ("Can't connect to GPM in batch mode.");
540         }
541
542         if (!console_name)
543         {
544                 /* Something seriously wrong here... */
545                 return (Qnil);
546         }
547
548         memset (process_name, '\0', sizeof(process_name));
549         snprintf (process_name, sizeof(process_name) - 1, "gpm for %s", console_name);
550
551         if (NILP (arg))
552         {
553                 turn_off_gpm (process_name);
554                 return (Qnil);
555         }
556
557         /* DANGER DANGER.
558         ** Though shalt not call (gpm-enable t) after we have already
559         ** started, or stuff blows up.
560         */
561         if (!NILP (Fgpm_enabled_p (device)))
562         {
563                 error ("GPM already enabled for this console.");
564         }
565
566         conn.eventMask = GPM_DOWN|GPM_UP|GPM_MOVE|GPM_DRAG;
567         conn.defaultMask = GPM_MOVE;
568         conn.minMod = 0;
569         conn.maxMod = ((1<<KG_SHIFT)|(1<<KG_ALT)|(1<<KG_CTRL));
570
571         /* Reset some silly static variables so that multiple Gpm_Open()
572         ** calls have even a sligh chance of working
573         */
574         gpm_tried = 0;
575         gpm_flag = 0;
576         gpm_stack = NULL;
577
578         /* Make sure Gpm_Open() does ioctl() on the correct
579         ** descriptor, or it can get the wrong terminal sizes, etc.
580         */
581         gpm_consolefd = fd;
582
583         /* We have to pass the virtual console manually, otherwise if you
584         ** use 'gnuclient -nw' to connect to an XEmacs that is running in
585         ** X, Gpm_Open() tries to use ttyname(0 | 1 | 2) to find out which
586         ** console you are using, which is of course not correct for the
587         ** new tty device.
588         */
589         if (strncmp (console_name, "/dev/tty",8) || !isdigit (console_name[8]))
590         {
591                 /* Urk, something really wrong */
592                 return (Qnil);
593         }
594
595         rval = Gpm_Open (&conn, atoi(console_name + 8));
596
597         switch (rval) {
598         case -1: /* General failure */
599                 break;
600         case -2: /* We are running under an XTerm */
601                 Gpm_Close();
602                 break;
603         default:
604                 /* Is this really necessary? */
605                 set_descriptor_non_blocking (gpm_fd);
606                 store_gpm_state (gpm_fd);
607                 gpm_process = connect_to_file_descriptor (build_string (process_name), Qnil,
608                                                                                                   make_int (gpm_fd),
609                                                                                                   make_int (gpm_fd));
610
611                 if (!NILP (gpm_process))
612                 {
613                         rval = 0;
614                         Fprocess_kill_without_query (gpm_process, Qnil);
615                         XSETSUBR (gpm_filter, &SFreceive_gpm_event);
616                         set_process_filter (gpm_process, gpm_filter, 1);
617
618                         /* Keep track of the device for later */
619                         /* Fput (gpm_process, intern ("gpm-device"), device); */
620                 }
621                 else
622                 {
623                         Gpm_Close();
624                         rval = -1;
625                 }
626         }
627
628         return(rval ? Qnil : Qt);
629 }
630
631 void vars_of_gpmevent (void)
632 {
633         gpm_event_queue = Qnil;
634         gpm_event_queue_tail = Qnil;
635         staticpro (&gpm_event_queue);
636         staticpro (&gpm_event_queue_tail);
637         pdump_wire (&gpm_event_queue);
638         pdump_wire (&gpm_event_queue_tail);
639 }
640
641 void syms_of_gpmevent (void)
642 {
643         DEFSUBR (Freceive_gpm_event);
644         DEFSUBR (Fgpm_enable);
645         DEFSUBR (Fgpm_enabled_p);
646 }
647
648 #endif /* HAVE_GPM */