Switch license to GPLv3 or later.
[bpt/emacs.git] / lib-src / emacstool.c
CommitLineData
a2535589 1/*
b3d90e46
GM
2 Copyright (C) 1986, 1988, 1990, 1991, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 Free Software Foundation, Inc.
3b48c1f4 4
3b7ad313 5This file is part of GNU Emacs.
91264a4a 6
3b7ad313
EN
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
4a9f99bd 9the Free Software Foundation; either version 3, or (at your option)
3b7ad313 10any later version.
91264a4a 11
3b7ad313
EN
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
364c38d3
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
91264a4a
DM
21
22/*
b92a7437 23 * For Emacs in SunView/Sun-Windows: (supported by Sun Unix v3.2 or greater)
5b2b0b72 24 * Insert a notifier filter-function to convert all useful input
a2535589
JA
25 * to "key" sequences that emacs can understand. See: Emacstool(1).
26 *
b92a7437 27 * Author: Jeff Peck, Sun Microsystems, Inc. <peck@eng.sun.com>
a2535589
JA
28 *
29 * Original Idea: Ian Batten
30 * Updated 15-Mar-88, Jeff Peck: set IN_EMACSTOOL, TERM, TERMCAP
b92a7437
JB
31 * Updated 10-Sep-88, Jeff Peck: add XVIEW and JLE support
32 * Updated 8-Oct-90, Jeff Peck: add Meta-bit for Xview
33 * Updated 6-Mar-91, Jeff Peck: Hack to detect -Wt invocation
34 * [note, TTYSW limitation means you must Click-To-Type in Openwin]
35 * [fixed in OW3 or use local/tty.o]
36 * for better results, this should move to using TERMSW.
37 * Updated 10-Mar-91, Jeff Peck, et al: support for TERMSW (TTERM)
38 * allows point-to-type even in OW2
39 *
40 * [note: xvetool should be started with the "-nw" flag for emacs!]
a2535589
JA
41 */
42
b92a7437
JB
43#ifdef XVIEW
44#include <xview/xview.h>
45#include <xview/panel.h>
46#include <xview/attr.h>
47#include <xview/tty.h>
48#include <xview/ttysw.h> /* private defines */
49#include <xview/termsw.h> /* -DTTERM */
50#include <xview/font.h> /* for testing */
51#else
a2535589
JA
52#include <suntool/sunview.h>
53#include <suntool/tty.h>
b92a7437
JB
54#include <suntool/ttysw.h>
55#endif XVIEW
56
57#ifdef JLE
58# include <locale.h>
59#endif JLE
60
a2535589
JA
61#include <stdio.h>
62#include <sys/file.h>
63
64#define BUFFER_SIZE 128 /* Size of all the buffers */
65
66/* define WANT_CAPS_LOCK to make f-key T1 (aka F1) behave as CapsLock */
67#define WANT_CAPS_LOCK
68#ifdef WANT_CAPS_LOCK
eb8c3be9 69int caps_lock; /* toggle indicator for f-key T1 caps lock */
a2535589
JA
70static char *Caps = "[CAPS] "; /* Caps Lock prefix string */
71#define CAPS_LEN 7 /* strlen (Caps) */
72#endif
73
74static char *mouse_prefix = "\030\000"; /* C-x C-@ */
75static int m_prefix_length = 2; /* mouse_prefix length */
76
77static char *key_prefix = "\030*"; /* C-x * */
78static int k_prefix_length = 2; /* key_prefix length */
79
b92a7437
JB
80#ifdef JLE
81static char *emacs_name = "nemacs"; /* default run command */
82static char *title = "NEmacstool - "; /* initial title */
83#else
a2535589 84static char *emacs_name = "emacs"; /* default run command */
a2535589 85static char *title = "Emacstool - "; /* initial title */
b92a7437
JB
86#endif JLE
87
88static char buffer[BUFFER_SIZE]; /* send to ttysw_input */
89static char *bold_name = 0; /* for -bold option */
a2535589
JA
90
91Frame frame; /* Base frame for system */
b92a7437
JB
92
93#ifndef TTERM
94#define SWTYPE TTY
95Tty tty_win; /* Where emacs is reading */
96#else
97#define SWTYPE TERMSW
98Termsw tty_win; /* Termsw does follow-mouse */
99#endif TTERM
100
101#ifdef XVIEW
102Xv_Window tty_view; /* Where the events are in Xview*/
103#else
104Tty tty_view; /* SunView place filler */
105#endif XVIEW
106
a2535589 107int font_width, font_height; /* For translating pixels to chars */
b92a7437 108int left_margin = 0; /* default window -- frame offset */
a2535589
JA
109
110int console_fd = 0; /* for debugging: setenv DEBUGEMACSTOOL */
111FILE *console; /* for debugging: setenv DEBUGEMACSTOOL */
112
113Icon frame_icon;
114/* make an icon_image for the default frame_icon */
5b2b0b72 115static short default_image[258] =
a2535589
JA
116{
117#include <images/terminal.icon>
118};
119mpr_static(icon_image, 64, 64, 1, default_image);
a2535589
JA
120\f
121/*
122 * Assign a value to a set of keys
123 */
124int
125button_value (event)
126 Event *event;
127{
128 int retval = 0;
129 /*
130 * Code up the current situation:
131 *
132 * 1 = MS_LEFT;
133 * 2 = MS_MIDDLE;
134 * 4 = MS_RIGHT;
135 * 8 = SHIFT;
136 * 16 = CONTROL;
137 * 32 = META;
138 * 64 = DOUBLE;
139 * 128 = UP;
140 */
141
142 if (MS_LEFT == (event_id (event))) retval = 1;
143 if (MS_MIDDLE == (event_id (event))) retval = 2;
144 if (MS_RIGHT == (event_id (event))) retval = 4;
145
146 if (event_shift_is_down (event)) retval += 8;
147 if (event_ctrl_is_down (event)) retval += 16;
148 if (event_meta_is_down (event)) retval += 32;
149 if (event_is_up (event)) retval += 128;
150 return retval;
151}
152\f
153/*
154 * Variables to store the time of the previous mouse event that was
155 * sent to emacs.
156 *
8e6208c5 157 * The theory is that to time double clicks while ignoring UP buttons,
a2535589
JA
158 * we must keep track of the accumulated time.
159 *
160 * If someone writes a SUN-SET-INPUT-MASK for emacstool,
5b2b0b72 161 * That could be used to selectively disable UP events,
a2535589
JA
162 * and then this cruft wouldn't be necessary.
163 */
164static long prev_event_sec = 0;
165static long prev_event_usec = 0;
166
167/*
168 * Give the time difference in milliseconds, where one second
169 * is considered infinite.
170 */
171int
172time_delta (now_sec, now_usec, prev_sec, prev_usec)
173 long now_sec, now_usec, prev_sec, prev_usec;
174{
175 long sec_delta = now_sec - prev_sec;
176 long usec_delta = now_usec - prev_usec;
5b2b0b72 177
a2535589
JA
178 if (usec_delta < 0) { /* "borrow" a second */
179 usec_delta += 1000000;
180 --sec_delta;
181 }
5b2b0b72
PJ
182
183 if (sec_delta >= 10)
a2535589
JA
184 return (9999); /* Infinity */
185 else
186 return ((sec_delta * 1000) + (usec_delta / 1000));
187}
188
189\f
190/*
191 * Filter function to translate selected input events for emacs
192 * Mouse button events become ^X^@(button x-col y-line time-delta) .
177c0ea7 193 * Function keys: ESC-*{c}{lrt} l,r,t for Left, Right, Top;
a2535589
JA
194 * {c} encodes the keynumber as a character [a-o]
195 */
196static Notify_value
197input_event_filter_function (window, event, arg, type)
b92a7437
JB
198#ifdef XVIEW
199 Xv_Window window;
200#else
a2535589 201 Window window;
b92a7437 202#endif XVIEW
a2535589
JA
203 Event *event;
204 Notify_arg arg;
205 Notify_event_type type;
206{
207 struct timeval time_stamp;
208
209 if (console_fd) fprintf(console, "Event: %d\n", event_id(event));
210
211 /* UP L1 is the STOP key */
212 if (event_id(event) == WIN_STOP) {
b92a7437 213 ttysw_input(tty_win, "\007\007\007\007\007\007\007", 7);
a2535589
JA
214 return NOTIFY_IGNORED;
215 }
216
217 /* UP L5 & L7 is Expose & Open, let them pass to sunview */
218 if (event_id(event) == KEY_LEFT(5) || event_id(event) == KEY_LEFT(7))
5b2b0b72 219 if(event_is_up (event))
a2535589
JA
220 return notify_next_event_func (window, event, arg, type);
221 else return NOTIFY_IGNORED;
222
223 if (event_is_button (event)) { /* do Mouse Button events */
224/* Commented out so that we send mouse up events too.
5b2b0b72 225 if (event_is_up (event))
a2535589
JA
226 return notify_next_event_func (window, event, arg, type);
227*/
228 time_stamp = event_time (event);
b92a7437 229 ttysw_input (tty_win, mouse_prefix, m_prefix_length);
5b2b0b72 230 sprintf (buffer, "(%d %d %d %d)\015",
a2535589 231 button_value (event),
b92a7437 232 (event_x (event) - left_margin) / font_width,
a2535589
JA
233 event_y (event) / font_height,
234 time_delta (time_stamp.tv_sec, time_stamp.tv_usec,
235 prev_event_sec, prev_event_usec)
236 );
b92a7437 237 ttysw_input (tty_win, buffer, strlen(buffer));
a2535589
JA
238 prev_event_sec = time_stamp.tv_sec;
239 prev_event_usec = time_stamp.tv_usec;
240 return NOTIFY_IGNORED;
241 }
5b2b0b72 242
a2535589
JA
243 { /* Do the function key events */
244 int d;
245 char c = (char) 0;
246 if ((event_is_key_left (event)) ?
5b2b0b72 247 ((d = event_id(event) - KEY_LEFT(1) + 'a'), c='l') :
a2535589 248 ((event_is_key_right (event)) ?
5b2b0b72 249 ((d = event_id(event) - KEY_RIGHT(1) + 'a'), c='r') :
a2535589
JA
250 ((event_is_key_top (event)) ?
251 ((d = event_id(event) - KEY_TOP(1) + 'a'), c='t') : 0)))
252 {
253 if (event_is_up(event)) return NOTIFY_IGNORED;
254 if (event_shift_is_down (event)) c = c - 32;
255 /* this will give a non-{lrt} for unshifted keys */
256 if (event_ctrl_is_down (event)) c = c - 64;
257 if (event_meta_is_down (event)) c = c + 128;
258#ifdef WANT_CAPS_LOCK
259/* set a toggle and relabel window so T1 can act like caps-lock */
5b2b0b72 260 if (event_id(event) == KEY_TOP(1))
a2535589
JA
261 {
262 /* make a frame label with and without CAPS */
5b2b0b72 263 strcpy (buffer, Caps);
a2535589
JA
264 title = &buffer[CAPS_LEN];
265 strncpy (title, (char *)window_get (frame, FRAME_LABEL),
266 BUFFER_SIZE - CAPS_LEN);
5b2b0b72 267 buffer[BUFFER_SIZE] = (char) 0;
a2535589
JA
268 if (strncmp (title, Caps, CAPS_LEN) == 0)
269 title += CAPS_LEN; /* already Caps */
270 caps_lock = (caps_lock ? 0 : CAPS_LEN);
271 window_set(frame, FRAME_LABEL, (title -= caps_lock), 0);
272 return NOTIFY_IGNORED;
273 }
274#endif
b92a7437 275 ttysw_input (tty_win, key_prefix, k_prefix_length);
a2535589 276 sprintf (buffer, "%c%c", d, c);
b92a7437 277 ttysw_input(tty_win, buffer, strlen(buffer));
a2535589
JA
278
279 return NOTIFY_IGNORED;
280 }
281 }
5b2b0b72 282 if ((event_is_ascii(event) || event_is_meta(event))
a2535589
JA
283 && event_is_up(event)) return NOTIFY_IGNORED;
284#ifdef WANT_CAPS_LOCK
285/* shift alpha chars to upper case if toggle is set */
286 if ((caps_lock) && event_is_ascii(event)
287 && (event_id(event) >= 'a') && (event_id(event) <= 'z'))
288 event_set_id(event, (event_id(event) - 32));
289/* crufty, but it works for now. is there an UPCASE(event)? */
b92a7437
JB
290#endif
291#ifndef NO_META_BIT
292/* under Openwindows/X, the meta bit is not set in the key event,
293 * emacs expects this so we add it in here:
294 */
295 if (event_is_ascii(event) && event_meta_is_down(event))
296 event_set_id(event, 128 | event_id(event));
a2535589
JA
297#endif
298 return notify_next_event_func (window, event, arg, type);
299}
300\f
301main (argc, argv)
302 int argc;
303 char **argv;
304{
305 int error_code; /* Error codes */
5b2b0b72 306
b92a7437
JB
307#ifdef JLE
308 setlocale(LC_ALL, "");
309#endif JLE
310
a2535589
JA
311 if(getenv("DEBUGEMACSTOOL"))
312 console = fdopen (console_fd = open("/dev/console",O_WRONLY), "w");
313
a2535589
JA
314 putenv("IN_EMACSTOOL=t"); /* notify subprocess that it is in emacstool */
315
b92a7437 316 if (putenv("TERM=sun") != 0) /* TTY_WIN will be a TERM=sun window */
a2535589
JA
317 {fprintf (stderr, "%s: Could not set TERM=sun, using `%s'\n",
318 argv[0], (char *)getenv("TERM")) ;};
319 /*
320 * If TERMCAP starts with a slash, it is the pathname of the
321 * termcap file, not an entry extracted from it, so KEEP it!
322 * Otherwise, it may not relate to the new TERM, so Nuke-It.
323 * If there is no TERMCAP environment variable, don't make one.
324 */
325 {
326 char *termcap ; /* Current TERMCAP value */
327 termcap = (char *)getenv("TERMCAP") ;
328 if (termcap && (*termcap != '/'))
329 {
330 if (putenv("TERMCAP=") != 0)
331 {fprintf (stderr, "%s: Could not clear TERMCAP\n", argv[0]) ;} ;
332 } ;
333 } ;
5b2b0b72 334
a2535589 335 /* find command to run as subprocess in window */
b92a7437 336 if (!(argv[0] = (char *)getenv("EMACSTOOL"))) /* Set emacs command name */
5b2b0b72 337 argv[0] = emacs_name;
b92a7437 338 /* Emacstool recognizes two special args: -rc <file> and -bold <bold-name> */
a2535589 339 for (argc = 1; argv[argc]; argc++) /* Use last one on line */
b92a7437
JB
340 {
341 if(!(strcmp ("-rc", argv[argc]))) /* Override if -rc given */
342 {int i = argc;
343 argv[argc--]=0; /* kill the -rc argument */
344 if (argv[i+1]) { /* move to argv[0] and squeeze the rest */
345 argv[0]=argv[i+1];
346 for (; argv[i+2]; (argv[i]=argv[i+2],argv[++i]=0));
347 }
348 }
349
5b2b0b72 350 if (!(strcmp ("-bold", argv[argc])))
b92a7437
JB
351 {int i = argc;
352 argv[argc--]=0; /* kill the -bold argument */
353 if (argv[i+1]) { /* move to bold_name and squeeze the rest */
354 bold_name = argv[i+1];
355 for (; argv[i+2]; (argv[i]=argv[i+2],argv[++i]=0));
356 }
357 }
358 };
a2535589
JA
359
360 strcpy (buffer, title);
361 strncat (buffer, argv[0], /* append run command name */
362 (BUFFER_SIZE - (strlen (buffer)) - (strlen (argv[0]))) - 1);
363
b92a7437
JB
364 error_code = interpose_on_window(argc,argv);
365 if (error_code != 0) { /* Barf */
366 fprintf (stderr, "notify_interpose_event_func returns %d.\n", error_code);
367 exit (1);
368 }
369
370#ifdef XVIEW
371 xv_main_loop (frame); /* And away we go */
372#else
373 window_main_loop (frame);
374#endif XVIEW
375}
a2535589 376
b92a7437
JB
377#ifdef XVIEW
378int interpose_on_window(argc,argv)
379 int argc;
380 char **argv;
381{
382#ifndef TTERM
8e6208c5 383 int i, font_width_adjust = 1; /* hackery, and heuristics */
b92a7437
JB
384 /* if -Wt is not supplied, then font comes out as lucida-14 (width=8)
385 * rather than the screen.r.12 (width=7) typically used
386 * this hack attempts to workaround it.
387 * could use a env var EMACSTOOL_DEFAULT_FONT_WIDTH instead */
388 for (i = 1; argv[i]; i++) {
389 if (!(strcmp ("-Wt", argv[i])))
390 {font_width_adjust = 0;
391 if (console_fd) fprintf(console, "-Wt = %d\n", font_width_adjust);
392 break;}
393 }
394#endif TTERM
395 /* initialize Xview, and strip window args */
396 xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, 0);
397
398 /* do this first, so arglist can override it */
399 frame_icon = icon_create (ICON_LABEL, "Emacstool",
400 ICON_IMAGE, &icon_image,
401 0);
402
403 /* Build a frame to run in */
404 frame = xv_create ((Xv_Window)NULL, FRAME,
405 FRAME_LABEL, buffer,
406 FRAME_ICON, frame_icon,
407 0);
408
409 /* Create a tty with emacs in it */
410 tty_win = xv_create (frame, SWTYPE, WIN_IS_CLIENT_PANE,
5b2b0b72 411 TTY_QUIT_ON_CHILD_DEATH, TRUE,
b92a7437 412 TTY_BOLDSTYLE, TTYSW_BOLD_INVERT,
5b2b0b72 413 TTY_ARGV, argv,
a2535589
JA
414 0);
415
b92a7437
JB
416 if (bold_name) {
417 (void)xv_set(tty_win, TTY_BOLDSTYLE_NAME, bold_name, 0);
418 }
419
a2535589 420 {
b92a7437
JB
421 Xv_font font; /* declare temp font variable */
422 font = (Xv_font)xv_get (tty_win, XV_FONT);
423 font_height = (int)xv_get (font, FONT_DEFAULT_CHAR_HEIGHT);
424 font_width = (int)xv_get (font, FONT_DEFAULT_CHAR_WIDTH);
425 }
426 if (console_fd) fprintf(console, "Width = %d\n", font_width);
427
428#ifndef TTERM
429 font_width -= font_width_adjust; /* A guess! font bug in ttysw*/
430#else
431 /* make the termsw act as a tty */
432 xv_set(tty_win, TERMSW_MODE, TTYSW_MODE_TYPE, 0);
433 /* termsw has variable offset depending on scrollbar size/location */
434 left_margin = (int)xv_get (tty_win, TEXTSW_LEFT_MARGIN);
435#endif TTERM
436
437 tty_view = (Xv_Window) xv_get (tty_win, OPENWIN_NTH_VIEW, 0);
438 xv_set(tty_view,
5b2b0b72 439 WIN_CONSUME_EVENTS,
b92a7437
JB
440 WIN_MOUSE_BUTTONS, WIN_UP_EVENTS,
441 ACTION_ADJUST, ACTION_MENU,
5b2b0b72 442 WIN_ASCII_EVENTS,
b92a7437
JB
443 WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS,
444 0,
445 0);
446 /* Interpose my event function */
5b2b0b72 447 return (int) notify_interpose_event_func
b92a7437
JB
448 (tty_view, input_event_filter_function, NOTIFY_SAFE);
449}
450#else
451int interpose_on_window (argc, argv)
452 int argc;
453 char **argv;
454{
455 /* do this first, so arglist can override it */
456 frame_icon = icon_create (ICON_LABEL, "Emacstool",
457 ICON_IMAGE, &icon_image,
458 0);
459
460 /* Build a frame to run in */
461 frame = window_create ((Window)NULL, FRAME,
462 FRAME_LABEL, buffer,
463 FRAME_ICON, frame_icon,
464 FRAME_ARGC_PTR_ARGV, &argc, argv,
465 0);
466
467 /* Create a tty with emacs in it */
f22d4785
PJ
468 tty_win = window_create (frame, TTY,
469 TTY_QUIT_ON_CHILD_DEATH, TRUE,
b92a7437 470 TTY_BOLDSTYLE, TTYSW_BOLD_INVERT,
f22d4785 471 TTY_ARGV, argv,
b92a7437
JB
472 0);
473
474 if (bold_name) {
475 (void)window_set(tty_win, TTY_BOLDSTYLE_NAME, bold_name, 0);
a2535589
JA
476 }
477
b92a7437
JB
478 /* ttysw uses pf_default, one must set WIN_FONT explicitly */
479 window_set (tty_win, WIN_FONT, pf_default(), 0);
480 font_height = (int)window_get (tty_win, WIN_ROW_HEIGHT);
481 font_width = (int)window_get (tty_win, WIN_COLUMN_WIDTH);
482
483 tty_view = tty_win;
484 window_set(tty_view,
5b2b0b72 485 WIN_CONSUME_PICK_EVENTS,
b92a7437
JB
486 WIN_STOP,
487 WIN_MOUSE_BUTTONS, WIN_UP_EVENTS,
488 /* LOC_WINENTER, LOC_WINEXIT, LOC_MOVE, */
489 0,
5b2b0b72 490 WIN_CONSUME_KBD_EVENTS,
b92a7437 491 WIN_STOP,
5b2b0b72 492 WIN_ASCII_EVENTS,
b92a7437
JB
493 WIN_LEFT_KEYS, WIN_TOP_KEYS, WIN_RIGHT_KEYS,
494 /* WIN_UP_ASCII_EVENTS, */
495 0,
496 0);
497 /* Interpose my event function */
5b2b0b72 498 return (int) notify_interpose_event_func
b92a7437 499 (tty_view, input_event_filter_function, NOTIFY_SAFE);
a2535589 500}
b92a7437 501#endif XVIEW
ab5796a9
MB
502
503/* arch-tag: 7a2e7105-c059-418a-b3d9-5b5de96abb4e
504 (do not change this comment) */