1 /* GPM (General purpose mouse) functions
2 Copyright (C) 1997 William M. Perry <wmperry@gnu.org>
3 Copyright (C) 1999 Free Software Foundation, Inc.
5 This file is part of SXEmacs
7 SXEmacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 /* Synched up with: Not in FSF. */
23 /* Authors: William Perry */
27 #include "ui/console.h"
28 #include "console-tty.h"
29 #include "ui/device.h"
30 #define INCLUDE_EVENTS_H_PRIVATE_SPHERE
31 #include "events/events.h"
32 #include "events/events-mod.h"
36 #include "sysproc.h" /* for MAXDESC */
43 #if (!defined(__linux__)) /* possible under xterm */
48 #include <linux/keyboard.h>
52 extern void *gpm_stack;
54 static int (*orig_event_pending_p) (int);
55 static void (*orig_next_event_cb) (Lisp_Event *);
57 static Lisp_Object gpm_event_queue;
58 static Lisp_Object gpm_event_queue_tail;
66 static struct __gpm_state gpm_state_information[MAXDESC];
68 static void store_gpm_state(int fd)
70 gpm_state_information[fd].gpm_tried = gpm_tried;
71 gpm_state_information[fd].gpm_flag = gpm_flag;
72 gpm_state_information[fd].gpm_stack = gpm_stack;
75 static void restore_gpm_state(int fd)
77 gpm_tried = gpm_state_information[fd].gpm_tried;
78 gpm_flag = gpm_state_information[fd].gpm_flag;
79 gpm_stack = gpm_state_information[fd].gpm_stack;
80 gpm_consolefd = gpm_fd = fd;
83 static void clear_gpm_state(int fd)
86 memset(&gpm_state_information[fd], '\0',
87 sizeof(struct __gpm_state));
89 gpm_tried = gpm_flag = 1;
90 gpm_fd = gpm_consolefd = -1;
94 static int get_process_infd(Lisp_Process * p)
96 Lisp_Object instr, outstr;
97 get_process_streams(p, &instr, &outstr);
99 return Lstream_get_fd(XLSTREAM(instr));
102 DEFUN("receive-gpm-event", Freceive_gpm_event, 0, 2, 0, /*
104 This function is the process handler for the GPM connection.
111 Lisp_Object fake_event = Qnil;
112 Lisp_Event *event = NULL;
114 static int num_events;
116 CHECK_PROCESS(process);
118 restore_gpm_state(get_process_infd(XPROCESS(process)));
120 if (!Gpm_GetEvent(&ev)) {
121 warn_when_safe(Qnil, Qcritical, "Gpm_GetEvent failed - %d",
130 fake_event = Fmake_event(Qnil, Qnil);
131 event = XEVENT(fake_event);
133 event->timestamp = 0;
134 event->channel = Fselected_frame(Qnil); /* CONSOLE_SELECTED_FRAME (con); */
136 /* Whow, wouldn't named defines be NICE!?!?! */
139 if (ev.modifiers & 1)
140 modifiers |= XEMACS_MOD_SHIFT;
141 if (ev.modifiers & 2)
142 modifiers |= XEMACS_MOD_META;
143 if (ev.modifiers & 4)
144 modifiers |= XEMACS_MOD_CONTROL;
145 if (ev.modifiers & 8)
146 modifiers |= XEMACS_MOD_META;
148 if (ev.buttons & GPM_B_LEFT) {
150 } else if (ev.buttons & GPM_B_MIDDLE) {
152 } else if (ev.buttons & GPM_B_RIGHT) {
156 switch (GPM_BARE_EVENTS(ev.type)) {
161 type & GPM_DOWN) ? button_press_event :
162 button_release_event;
163 event->event.button.x = ev.x;
164 event->event.button.y = ev.y;
165 event->event.button.button = button;
166 event->event.button.modifiers = modifiers;
170 event->event_type = pointer_motion_event;
171 event->event.motion.x = ev.x;
172 event->event.motion.y = ev.y;
173 event->event.motion.modifiers = modifiers;
175 /* This will never happen */
179 /* Handle the event */
180 enqueue_event(fake_event, &gpm_event_queue, &gpm_event_queue_tail);
187 static void turn_off_gpm(char *process_name)
189 Lisp_Object process = Fget_process(build_string(process_name));
193 /* Something happened to our GPM process - fail silently */
197 fd = get_process_infd(XPROCESS(process));
199 restore_gpm_state(fd);
205 Fdelete_process(build_string(process_name));
210 tty_get_foreign_selection(Lisp_Object selection_symbol, Lisp_Object target_type)
212 /* This function can GC */
213 struct device *d = decode_device(Qnil);
214 int fd = DEVICE_INFD(d);
216 Lisp_Object output_stream = Qnil;
217 Lisp_Object terminal_stream = Qnil;
218 Lisp_Object output_string = Qnil;
219 struct gcpro gcpro1, gcpro2, gcpro3;
221 GCPRO3(output_stream, terminal_stream, output_string);
223 /* The ioctl() to paste actually puts things in the input queue of
224 ** the virtual console, so we need to trap that data, since we are
225 ** supposed to return the actual string selection from this
229 /* I really hate doing this, but it doesn't seem to cause any
230 ** problems, and it makes the Lstream_read stuff further down
231 ** error out correctly instead of trying to indefinitely read from
234 ** There is no set_descriptor_blocking() function call, but in my
235 ** testing under linux, it has not proved fatal to leave the
236 ** descriptor in non-blocking mode.
238 ** William Perry Nov 5, 1999
240 set_descriptor_non_blocking(fd);
242 /* We need two streams, one for reading from the selected device,
243 ** and one to write the data into. There is no writable version
244 ** of the lisp-string lstream, so we make do with a resizing
245 ** buffer stream, and make a string out of it after we are
248 output_stream = make_resizing_buffer_output_stream();
250 make_filedesc_input_stream(fd, 0, -1, LSTR_BLOCKED_OK);
251 output_string = Qnil;
253 /* #### We should arguably use a specbind() and an unwind routine here,
254 ** #### but I don't care that much right now.
256 if (NILP(output_stream) || NILP(terminal_stream)) {
257 /* Should we signal an error here? */
261 if (ioctl(fd, TIOCLINUX, &c) < 0) {
262 /* Could not get the selection - eek */
268 Bufbyte tempbuf[1024]; /* some random amount */
269 Lstream_data_count i;
270 Lstream_data_count size_in_bytes =
271 Lstream_read(XLSTREAM(terminal_stream),
272 tempbuf, sizeof(tempbuf));
274 if (size_in_bytes <= 0) {
275 /* end of the stream */
280 for (i = 0; i < size_in_bytes; i++) {
281 if (tempbuf[i] == '\r') {
286 Lstream_write(XLSTREAM(output_stream), tempbuf, size_in_bytes);
289 Lstream_flush(XLSTREAM(output_stream));
292 make_string(resizing_buffer_stream_ptr(XLSTREAM(output_stream)),
293 Lstream_byte_count(XLSTREAM(output_stream)));
295 Lstream_delete(XLSTREAM(output_stream));
296 Lstream_delete(XLSTREAM(terminal_stream));
300 return (output_string);
304 tty_selection_exists_p(Lisp_Object selection, Lisp_Object selection_type)
308 #endif /* TIOCLINUX */
312 tty_own_selection(Lisp_Object selection_name, Lisp_Object selection_value,
313 Lisp_Object how_to_add, Lisp_Object selection_type)
315 /* There is no way to do this cleanly - the GPM selection
316 ** 'protocol' (actually the TIOCLINUX ioctl) requires a start and
317 ** end position on the _screen_, not a string to stick in there.
320 ** William Perry Nov 4, 1999
325 /* This function appears to work once in a blue moon. I'm not sure
326 ** exactly why either. *sigh*
328 ** William Perry Nov 4, 1999
330 ** Apparently, this is the way (mouse-position) is supposed to work,
331 ** and I was just expecting something else. (mouse-pixel-position)
334 ** William Perry Nov 7, 1999
337 tty_get_mouse_position(struct device *d, Lisp_Object * frame, int *x, int *y)
342 memset(&ev, '\0', sizeof(ev));
344 num_buttons = Gpm_GetSnapshot(&ev);
347 /* This means there are events pending... */
349 /* #### In theory, we should drain the events pending, stick
350 ** #### them in the queue, and return the mouse position
357 *frame = DEVICE_SELECTED_FRAME(d);
361 static void tty_set_mouse_position(struct window *w, int x, int y)
364 #### I couldn't find any GPM functions that set the mouse position.
365 #### Mr. Perry had left this function empty; that must be why.
370 static int gpm_event_pending_p(int user_p)
374 EVENT_CHAIN_LOOP(event, gpm_event_queue) {
375 if (!user_p || command_event_p(event)) {
379 return (orig_event_pending_p(user_p));
382 static void gpm_next_event_cb(Lisp_Event * event)
384 /* #### It would be nice to preserve some sort of ordering of the
385 ** #### different types of events, but that would be quite a bit
386 ** #### of work, and would more than likely break the abstraction
387 ** #### between the other event loops and this one.
390 if (!NILP(gpm_event_queue)) {
391 Lisp_Object queued_event =
392 dequeue_event(&gpm_event_queue, &gpm_event_queue_tail);
393 *event = *(XEVENT(queued_event));
395 if (event->event_type == pointer_motion_event) {
396 struct device *d = decode_device(event->channel);
397 int fd = DEVICE_INFD(d);
399 /* Ok, now this is just freaky. Bear with me though.
401 ** If you run gnuclient and attach to a SXEmacs running in
402 ** X or on another TTY, the mouse cursor does not get
403 ** drawn correctly. This is because the ioctl() fails
404 ** with EPERM because the TTY specified is not our
405 ** controlling terminal. If you are the superuser, it
406 ** will work just spiffy. The appropriate source file (at
407 ** least in linux 2.2.x) is
408 ** .../linux/drivers/char/console.c in the function
409 ** tioclinux(). The following bit of code is brutal to
412 ** if (current->tty != tty && !suser())
415 ** I even tried setting us as a process leader, removing
416 ** our controlling terminal, and then using the TIOCSCTTY
417 ** to set up a new controlling terminal, all with no luck.
419 ** What is even weirder is if you run SXEmacs in a VC, and
420 ** attach to it from another VC with gnuclient, go back to
421 ** the original VC and hit a key, the mouse pointer
422 ** displays (in BOTH VCs), until you hit a key in the
423 ** second VC, after which it does not display in EITHER
426 ** All I can say is thank god Linux comes with source code
427 ** or I would have been completely confused. Well, ok,
428 ** I'm still completely confused. I don't see why they
429 ** don't just check the permissions on the device
430 ** (actually, if you have enough access to it to get the
431 ** console's file descriptor, you should be able to do
432 ** with it as you wish, but maybe that is just me).
434 ** William M. Perry - Nov 9, 1999
437 Gpm_DrawPointer(event->event.motion.x,
438 event->event.motion.y, fd);
444 orig_next_event_cb(event);
447 static void hook_event_callbacks_once(void)
452 orig_event_pending_p = event_stream->event_pending_p;
453 orig_next_event_cb = event_stream->next_event_cb;
454 event_stream->event_pending_p = gpm_event_pending_p;
455 event_stream->next_event_cb = gpm_next_event_cb;
460 static void hook_console_methods_once(void)
465 /* Install the mouse position methods for the TTY console type */
466 CONSOLE_HAS_METHOD(tty, get_mouse_position);
467 CONSOLE_HAS_METHOD(tty, set_mouse_position);
468 CONSOLE_HAS_METHOD(tty, get_foreign_selection);
469 CONSOLE_HAS_METHOD(tty, selection_exists_p);
471 CONSOLE_HAS_METHOD(tty, own_selection);
476 DEFUN("gpm-enabled-p", Fgpm_enabled_p, 0, 1, 0, /*
477 Return non-nil if GPM mouse support is currently enabled on DEVICE.
481 char *console_name = ttyname(DEVICE_INFD(decode_device(device)));
482 char process_name[1024];
489 memset(process_name, '\0', sizeof(process_name));
490 snprintf(process_name, sizeof(process_name) - 1, "gpm for %s",
493 proc = Fget_process(build_string(process_name));
499 if (1) { /* (PROCESS_LIVE_P (proc)) */
505 DEFUN("gpm-enable", Fgpm_enable, 0, 2, 0, /*
506 Toggle accepting of GPM mouse events.
512 Lisp_Object gpm_process;
513 Lisp_Object gpm_filter;
514 struct device *d = decode_device(device);
515 int fd = DEVICE_INFD(d);
516 char *console_name = ttyname(fd);
517 char process_name[1024];
519 hook_event_callbacks_once();
520 hook_console_methods_once();
522 if (noninteractive) {
523 error("Can't connect to GPM in batch mode.");
527 /* Something seriously wrong here... */
531 memset(process_name, '\0', sizeof(process_name));
532 snprintf(process_name, sizeof(process_name) - 1, "gpm for %s",
536 turn_off_gpm(process_name);
541 ** Though shalt not call (gpm-enable t) after we have already
542 ** started, or stuff blows up.
544 if (!NILP(Fgpm_enabled_p(device))) {
545 error("GPM already enabled for this console.");
548 conn.eventMask = GPM_DOWN | GPM_UP | GPM_MOVE | GPM_DRAG;
549 conn.defaultMask = GPM_MOVE;
551 conn.maxMod = ((1 << KG_SHIFT) | (1 << KG_ALT) | (1 << KG_CTRL));
553 /* Reset some silly static variables so that multiple Gpm_Open()
554 ** calls have even a slight chance of working
560 /* Make sure Gpm_Open() does ioctl() on the correct
561 ** descriptor, or it can get the wrong terminal sizes, etc.
565 /* We have to pass the virtual console manually, otherwise if you
566 ** use 'gnuclient -nw' to connect to an SXEmacs that is running in
567 ** X, Gpm_Open() tries to use ttyname(0 | 1 | 2) to find out which
568 ** console you are using, which is of course not correct for the
571 if (strncmp(console_name, "/dev/tty", 8) || !isdigit(console_name[8])) {
572 /* Urk, something really wrong */
576 rval = Gpm_Open(&conn, atoi(console_name + 8));
579 case -1: /* General failure */
581 case -2: /* We are running under an XTerm */
585 /* Is this really necessary? */
586 set_descriptor_non_blocking(gpm_fd);
587 store_gpm_state(gpm_fd);
589 connect_to_file_descriptor(build_string(process_name), Qnil,
593 if (!NILP(gpm_process)) {
595 Fprocess_kill_without_query(gpm_process, Qnil);
596 XSETSUBR(gpm_filter, &SFreceive_gpm_event);
597 set_process_filter(gpm_process, gpm_filter, 1);
599 /* Keep track of the device for later */
600 /* Fput (gpm_process, intern ("gpm-device"), device); */
607 return (rval ? Qnil : Qt);
610 void vars_of_gpmevent(void)
612 gpm_event_queue = Qnil;
613 gpm_event_queue_tail = Qnil;
614 staticpro(&gpm_event_queue);
615 staticpro(&gpm_event_queue_tail);
616 dump_add_root_object(&gpm_event_queue);
617 dump_add_root_object(&gpm_event_queue_tail);
620 void syms_of_gpmevent(void)
622 DEFSUBR(Freceive_gpm_event);
623 DEFSUBR(Fgpm_enable);
624 DEFSUBR(Fgpm_enabled_p);
627 #endif /* HAVE_GPM */