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)
71 warn_when_safe(Qnil, Qcritical, "store_gpm_state negative fd - %d",
75 gpm_state_information[fd].gpm_tried = gpm_tried;
76 gpm_state_information[fd].gpm_flag = gpm_flag;
77 gpm_state_information[fd].gpm_stack = gpm_stack;
80 static void restore_gpm_state(int fd)
83 warn_when_safe(Qnil, Qcritical, "restore_gpm_state negative fd - %d",
87 gpm_tried = gpm_state_information[fd].gpm_tried;
88 gpm_flag = gpm_state_information[fd].gpm_flag;
89 gpm_stack = gpm_state_information[fd].gpm_stack;
90 gpm_consolefd = gpm_fd = fd;
93 static void clear_gpm_state(int fd)
96 memset(&gpm_state_information[fd], '\0',
97 sizeof(struct __gpm_state));
99 gpm_tried = gpm_flag = 1;
100 gpm_fd = gpm_consolefd = -1;
104 static int get_process_infd(Lisp_Process * p)
106 Lisp_Object instr, outstr;
107 get_process_streams(p, &instr, &outstr);
108 assert(!NILP(instr));
109 return Lstream_get_fd(XLSTREAM(instr));
112 DEFUN("receive-gpm-event", Freceive_gpm_event, 0, 2, 0, /*
114 This function is the process handler for the GPM connection.
121 Lisp_Object fake_event = Qnil;
122 Lisp_Event *event = NULL;
124 static int num_events;
126 CHECK_PROCESS(process);
128 restore_gpm_state(get_process_infd(XPROCESS(process)));
130 if (!Gpm_GetEvent(&ev)) {
131 warn_when_safe(Qnil, Qcritical, "Gpm_GetEvent failed - %d",
140 fake_event = Fmake_event(Qnil, Qnil);
141 event = XEVENT(fake_event);
143 event->timestamp = 0;
144 event->channel = Fselected_frame(Qnil); /* CONSOLE_SELECTED_FRAME (con); */
146 /* Whow, wouldn't named defines be NICE!?!?! */
149 if (ev.modifiers & 1)
150 modifiers |= XEMACS_MOD_SHIFT;
151 if (ev.modifiers & 2)
152 modifiers |= XEMACS_MOD_META;
153 if (ev.modifiers & 4)
154 modifiers |= XEMACS_MOD_CONTROL;
155 if (ev.modifiers & 8)
156 modifiers |= XEMACS_MOD_META;
158 if (ev.buttons & GPM_B_LEFT) {
160 } else if (ev.buttons & GPM_B_MIDDLE) {
162 } else if (ev.buttons & GPM_B_RIGHT) {
166 switch (GPM_BARE_EVENTS(ev.type)) {
171 type & GPM_DOWN) ? button_press_event :
172 button_release_event;
173 event->event.button.x = ev.x;
174 event->event.button.y = ev.y;
175 event->event.button.button = button;
176 event->event.button.modifiers = modifiers;
180 event->event_type = pointer_motion_event;
181 event->event.motion.x = ev.x;
182 event->event.motion.y = ev.y;
183 event->event.motion.modifiers = modifiers;
185 /* This will never happen */
189 /* Handle the event */
190 enqueue_event(fake_event, &gpm_event_queue, &gpm_event_queue_tail);
197 static void turn_off_gpm(char *process_name)
199 Lisp_Object process = Fget_process(build_string(process_name));
203 /* Something happened to our GPM process - fail silently */
207 fd = get_process_infd(XPROCESS(process));
209 restore_gpm_state(fd);
215 Fdelete_process(build_string(process_name));
220 tty_get_foreign_selection(Lisp_Object selection_symbol, Lisp_Object target_type)
222 /* This function can GC */
223 struct device *d = decode_device(Qnil);
224 int fd = DEVICE_INFD(d);
226 Lisp_Object output_stream = Qnil;
227 Lisp_Object terminal_stream = Qnil;
228 Lisp_Object output_string = Qnil;
229 struct gcpro gcpro1, gcpro2, gcpro3;
231 GCPRO3(output_stream, terminal_stream, output_string);
233 /* The ioctl() to paste actually puts things in the input queue of
234 ** the virtual console, so we need to trap that data, since we are
235 ** supposed to return the actual string selection from this
239 /* I really hate doing this, but it doesn't seem to cause any
240 ** problems, and it makes the Lstream_read stuff further down
241 ** error out correctly instead of trying to indefinitely read from
244 ** There is no set_descriptor_blocking() function call, but in my
245 ** testing under linux, it has not proved fatal to leave the
246 ** descriptor in non-blocking mode.
248 ** William Perry Nov 5, 1999
250 set_descriptor_non_blocking(fd);
252 /* We need two streams, one for reading from the selected device,
253 ** and one to write the data into. There is no writable version
254 ** of the lisp-string lstream, so we make do with a resizing
255 ** buffer stream, and make a string out of it after we are
258 output_stream = make_resizing_buffer_output_stream();
260 make_filedesc_input_stream(fd, 0, -1, LSTR_BLOCKED_OK);
261 output_string = Qnil;
263 /* #### We should arguably use a specbind() and an unwind routine here,
264 ** #### but I don't care that much right now.
266 if (NILP(output_stream) || NILP(terminal_stream)) {
267 /* Should we signal an error here? */
271 if (ioctl(fd, TIOCLINUX, &c) < 0) {
272 /* Could not get the selection - eek */
278 Bufbyte tempbuf[1024]; /* some random amount */
279 Lstream_data_count i;
280 Lstream_data_count size_in_bytes =
281 Lstream_read(XLSTREAM(terminal_stream),
282 tempbuf, sizeof(tempbuf));
284 if (size_in_bytes <= 0) {
285 /* end of the stream */
290 for (i = 0; i < size_in_bytes; i++) {
291 if (tempbuf[i] == '\r') {
296 Lstream_write(XLSTREAM(output_stream), tempbuf, size_in_bytes);
299 Lstream_flush(XLSTREAM(output_stream));
302 make_string(resizing_buffer_stream_ptr(XLSTREAM(output_stream)),
303 Lstream_byte_count(XLSTREAM(output_stream)));
305 Lstream_delete(XLSTREAM(output_stream));
306 Lstream_delete(XLSTREAM(terminal_stream));
310 return (output_string);
314 tty_selection_exists_p(Lisp_Object selection, Lisp_Object selection_type)
318 #endif /* TIOCLINUX */
322 tty_own_selection(Lisp_Object selection_name, Lisp_Object selection_value,
323 Lisp_Object how_to_add, Lisp_Object selection_type)
325 /* There is no way to do this cleanly - the GPM selection
326 ** 'protocol' (actually the TIOCLINUX ioctl) requires a start and
327 ** end position on the _screen_, not a string to stick in there.
330 ** William Perry Nov 4, 1999
335 /* This function appears to work once in a blue moon. I'm not sure
336 ** exactly why either. *sigh*
338 ** William Perry Nov 4, 1999
340 ** Apparently, this is the way (mouse-position) is supposed to work,
341 ** and I was just expecting something else. (mouse-pixel-position)
344 ** William Perry Nov 7, 1999
347 tty_get_mouse_position(struct device *d, Lisp_Object * frame, int *x, int *y)
352 memset(&ev, '\0', sizeof(ev));
354 num_buttons = Gpm_GetSnapshot(&ev);
357 /* This means there are events pending... */
359 /* #### In theory, we should drain the events pending, stick
360 ** #### them in the queue, and return the mouse position
367 *frame = DEVICE_SELECTED_FRAME(d);
371 static void tty_set_mouse_position(struct window *w, int x, int y)
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.
380 static int gpm_event_pending_p(int user_p)
384 EVENT_CHAIN_LOOP(event, gpm_event_queue) {
385 if (!user_p || command_event_p(event)) {
389 return (orig_event_pending_p(user_p));
392 static void gpm_next_event_cb(Lisp_Event * event)
394 /* #### It would be nice to preserve some sort of ordering of the
395 ** #### different types of events, but that would be quite a bit
396 ** #### of work, and would more than likely break the abstraction
397 ** #### between the other event loops and this one.
400 if (!NILP(gpm_event_queue)) {
401 Lisp_Object queued_event =
402 dequeue_event(&gpm_event_queue, &gpm_event_queue_tail);
403 *event = *(XEVENT(queued_event));
405 if (event->event_type == pointer_motion_event) {
406 struct device *d = decode_device(event->channel);
407 int fd = DEVICE_INFD(d);
409 /* Ok, now this is just freaky. Bear with me though.
411 ** If you run gnuclient and attach to a SXEmacs running in
412 ** X or on another TTY, the mouse cursor does not get
413 ** drawn correctly. This is because the ioctl() fails
414 ** with EPERM because the TTY specified is not our
415 ** controlling terminal. If you are the superuser, it
416 ** will work just spiffy. The appropriate source file (at
417 ** least in linux 2.2.x) is
418 ** .../linux/drivers/char/console.c in the function
419 ** tioclinux(). The following bit of code is brutal to
422 ** if (current->tty != tty && !suser())
425 ** I even tried setting us as a process leader, removing
426 ** our controlling terminal, and then using the TIOCSCTTY
427 ** to set up a new controlling terminal, all with no luck.
429 ** What is even weirder is if you run SXEmacs in a VC, and
430 ** attach to it from another VC with gnuclient, go back to
431 ** the original VC and hit a key, the mouse pointer
432 ** displays (in BOTH VCs), until you hit a key in the
433 ** second VC, after which it does not display in EITHER
436 ** All I can say is thank god Linux comes with source code
437 ** or I would have been completely confused. Well, ok,
438 ** I'm still completely confused. I don't see why they
439 ** don't just check the permissions on the device
440 ** (actually, if you have enough access to it to get the
441 ** console's file descriptor, you should be able to do
442 ** with it as you wish, but maybe that is just me).
444 ** William M. Perry - Nov 9, 1999
447 Gpm_DrawPointer(event->event.motion.x,
448 event->event.motion.y, fd);
454 orig_next_event_cb(event);
457 static void hook_event_callbacks_once(void)
462 orig_event_pending_p = event_stream->event_pending_p;
463 orig_next_event_cb = event_stream->next_event_cb;
464 event_stream->event_pending_p = gpm_event_pending_p;
465 event_stream->next_event_cb = gpm_next_event_cb;
470 static void hook_console_methods_once(void)
475 /* Install the mouse position methods for the TTY console type */
476 CONSOLE_HAS_METHOD(tty, get_mouse_position);
477 CONSOLE_HAS_METHOD(tty, set_mouse_position);
478 CONSOLE_HAS_METHOD(tty, get_foreign_selection);
479 CONSOLE_HAS_METHOD(tty, selection_exists_p);
481 CONSOLE_HAS_METHOD(tty, own_selection);
486 DEFUN("gpm-enabled-p", Fgpm_enabled_p, 0, 1, 0, /*
487 Return non-nil if GPM mouse support is currently enabled on DEVICE.
491 char *console_name = ttyname(DEVICE_INFD(decode_device(device)));
492 char process_name[1024];
500 sz = snprintf(process_name, sizeof(process_name), "gpm for %s",
502 assert(sz >= 0 && sz < sizeof(process_name));
504 proc = Fget_process(build_string(process_name));
510 if (1) { /* (PROCESS_LIVE_P (proc)) */
516 DEFUN("gpm-enable", Fgpm_enable, 0, 2, 0, /*
517 Toggle accepting of GPM mouse events.
523 Lisp_Object gpm_process;
524 Lisp_Object gpm_filter;
525 struct device *d = decode_device(device);
526 int fd = DEVICE_INFD(d);
527 char *console_name = ttyname(fd);
528 char process_name[1024];
530 hook_event_callbacks_once();
531 hook_console_methods_once();
533 if (noninteractive) {
534 error("Can't connect to GPM in batch mode.");
538 /* Something seriously wrong here... */
542 sz = snprintf(process_name, sizeof(process_name), "gpm for %s",
544 assert(sz >= 0 && sz < sizeof(process_name));
547 turn_off_gpm(process_name);
552 ** Though shalt not call (gpm-enable t) after we have already
553 ** started, or stuff blows up.
555 if (!NILP(Fgpm_enabled_p(device))) {
556 error("GPM already enabled for this console.");
559 conn.eventMask = GPM_DOWN | GPM_UP | GPM_MOVE | GPM_DRAG;
560 conn.defaultMask = GPM_MOVE;
562 conn.maxMod = ((1 << KG_SHIFT) | (1 << KG_ALT) | (1 << KG_CTRL));
564 /* Reset some silly static variables so that multiple Gpm_Open()
565 ** calls have even a slight chance of working
571 /* Make sure Gpm_Open() does ioctl() on the correct
572 ** descriptor, or it can get the wrong terminal sizes, etc.
576 /* We have to pass the virtual console manually, otherwise if you
577 ** use 'gnuclient -nw' to connect to an SXEmacs that is running in
578 ** X, Gpm_Open() tries to use ttyname(0 | 1 | 2) to find out which
579 ** console you are using, which is of course not correct for the
582 if (strncmp(console_name, "/dev/tty", 8) || !isdigit(console_name[8])) {
583 /* Urk, something really wrong */
587 rval = Gpm_Open(&conn, atoi(console_name + 8));
590 case -1: /* General failure */