Initial git import
[sxemacs] / src / ExternalClient.c
1 /* External client widget.
2    Copyright (C) 1993, 1994 Sun Microsystems, Inc.
3
4 This file is part of SXEmacs
5
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19
20 /* Synched up with: Not in FSF. */
21
22 /* Written by Ben Wing, September 1993. */
23
24 #ifdef emacs
25
26 #include <config.h>
27
28 #ifndef EXTERNAL_WIDGET
29 ERROR ! This ought not be getting compiled if EXTERNAL_WIDGET
30         is undefined
31 #endif
32 #endif                          /* emacs */
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #ifdef EXTW_USES_MOTIF
37 # include <Xm/XmP.h>
38 # include <Xm/PrimitiveP.h>
39 # include <X11/keysym.h>
40 #else
41 # include "xintrinsicp.h"
42 # include <X11/StringDefs.h>
43 #endif
44 #include "ExternalClientP.h"
45 #include "ui/X11/extw-Xt.h"
46 #ifdef TOOLTALK
47 #include TT_C_H_FILE
48 #endif
49 /* This is the client widget, used to communicate with an ExternalShell
50    widget. */
51 #define NOTIFY(w, type, l0, l1, l2) \
52   extw_send_notify_3(XtDisplay((Widget)(w)), XtWindow((Widget)(w)),\
53                      type, l0, l1, l2)
54         static void externalClientInitialize(Widget req, Widget new,
55                                              ArgList args, Cardinal * num_args);
56 static void externalClientRealize(Widget widget, XtValueMask * mask,
57                                   XSetWindowAttributes * attrs);
58 static void Destroy(Widget w);
59 static void EventHandler(Widget wid, XtPointer closure, XEvent * event,
60                          Boolean * continue_to_dispatch);
61 static void MaskableEventHandler(Widget wid, XtPointer closure, XEvent * event,
62                                  Boolean * continue_to_dispatch);
63 static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry *,
64                                       XtWidgetGeometry *);
65 static void ExternalClientFocusIn(Widget, XEvent *, String *, Cardinal *);
66 static void ExternalClientFocusOut(Widget, XEvent *, String *, Cardinal *);
67 static void ExternalClientEnter(Widget, XEvent *, String *, Cardinal *);
68 static void ExternalClientLeave(Widget, XEvent *, String *, Cardinal *);
69
70 static int my_error_handler(Display * display, XErrorEvent * xev);
71 static int (*error_old_handler) (Display *, XErrorEvent *);
72
73 static XtResource resources[] = {
74 #define offset(field) XtOffset(ExternalClientWidget, externalClient.field)
75         {XtNshellTimeout, XtCShellTimeout,
76          XtRInt, sizeof(int),
77          offset(shell_timeout), XtRImmediate, (XtPointer) DEFAULT_WM_TIMEOUT},
78         {XtNdeadShell, XtCDeadShell,
79          XtRBoolean, sizeof(Boolean),
80          offset(dead_shell), XtRImmediate, (XtPointer) False}
81         ,
82 #ifdef EXTW_USES_MOTIF
83         {XmNnavigationType, XmCNavigationType,
84          XmRNavigationType, sizeof(XmNavigationType),
85          XtOffset(ExternalClientWidget, primitive.navigation_type),
86          XtRImmediate, (XtPointer) XmTAB_GROUP}
87         ,
88 #endif
89         {XtNemacsProcID, XtCEmacsProcID,
90          XtRString, sizeof(String),
91          offset(emacs_procid), XtRImmediate, (XtPointer) NULL}
92         ,
93         {XtNshellReadyCallback, XtCCallback,
94          XtRCallback, sizeof(XtCallbackList),
95          offset(shell_ready_callback), XtRImmediate, (XtPointer) NULL}
96         ,
97         {XtNshellName, XtCShellName,
98          XtRString, sizeof(String),
99          offset(shell_name), XtRImmediate, (XtPointer) NULL}
100         ,
101         {XtNuseToolTalk, XtCUseToolTalk,
102          XtRBoolean, sizeof(Boolean),
103          offset(use_tooltalk), XtRImmediate, (XtPointer) False}
104 };
105
106 static XtActionsRec actions[] = {
107         {"focusIn", ExternalClientFocusIn},
108         {"focusOut", ExternalClientFocusOut},
109         {"enter", ExternalClientEnter},
110         {"leave", ExternalClientLeave},
111 };
112
113 ExternalClientClassRec externalClientClassRec = {
114         {                       /*
115                                  *        core_class fields
116                                  */
117 #ifdef EXTW_USES_MOTIF
118          /* superclass         */ (WidgetClass) & xmPrimitiveClassRec,
119 #else
120          /* superclass         */ (WidgetClass) & coreClassRec,
121 #endif
122          /* class_name         */ "ExternalClient",
123          /* size               */ sizeof(ExternalClientRec),
124          /* Class Initializer  */ NULL,
125                                                 /* class_part_initialize */ NULL,
126                                                 /* XtInheritClassPartInitialize, */
127          /* Class init'ed ?    */ FALSE,
128          /* initialize         */ externalClientInitialize,
129          /* initialize_notify  */ NULL,
130          /* realize            */ externalClientRealize,
131          /* actions            */ actions,
132          /* num_actions        */ XtNumber(actions),
133          /* resources          */ resources,
134          /* resource_count     */ XtNumber(resources),
135          /* xrm_class          */ NULLQUARK,
136          /* compress_motion    */ FALSE,
137          /* compress_exposure  */ TRUE,
138          /* compress_enterleave */ FALSE,
139          /* visible_interest   */ TRUE,
140                                                 /* destroy            */ Destroy,
141                                                 /* XtInheritDestroy, */
142          /* resize             */ XtInheritResize,
143          /* expose             */ NULL,
144                                         /* set_values         */ NULL,
145                                         /* XtInheritSetValues, */
146          /* set_values_hook    */ NULL,
147          /* set_values_almost  */ XtInheritSetValuesAlmost,
148          /* get_values_hook    */ NULL,
149          /* accept_focus       */ NULL,
150          /* intrinsics version */ XtVersion,
151          /* callback offsets   */ NULL,
152                                         /* tm_table           */ "",
153                                         /* MUST NOT BE NULL or
154                                            XtInheritTranslations in Motif!!!!!
155                                            Otherwise keyboard focus translations
156                                            will not work. */
157          /* query_geometry     */ QueryGeometry,
158          /* display_accelerator */ NULL,
159          /* extension          */ NULL
160          }
161         ,
162 #ifdef EXTW_USES_MOTIF
163         {
164          XmInheritBorderHighlight,      /* Primitive border_highlight */
165          XmInheritBorderHighlight,      /* Primitive border_unhighlight */
166          XtInheritTranslations, /* translations */
167          NULL,                  /* arm_and_activate */
168          NULL,                  /* get resources */
169          0,                     /* num get_resources */
170          NULL,                  /* extension */
171          }
172         ,
173 #endif
174         {
175          0}
176 };
177
178 WidgetClass externalClientWidgetClass = (WidgetClass) & externalClientClassRec;
179
180 static void
181 externalClientInitialize(Widget req, Widget new, ArgList args,
182                          Cardinal * num_args)
183 {
184         ExternalClientWidget ecw = (ExternalClientWidget) new;
185         static int error_handler_added = 0;
186
187         extw_initialize_atoms(XtDisplay(new));
188         extw_which_side = extw_client_send;
189
190 #ifdef EXTW_USES_MOTIF
191
192         /* yes I know this is horrible.  However, the XmPrimitive class adds
193            the Tab translation in its initialization routine, so we have to
194            override it here.  This is all the fault of Xt, which doesn't
195            provide a proper inheritance mechanism for translations.
196
197            -- BPW
198
199          */
200
201         XtOverrideTranslations(new,
202                                XtParseTranslationTable("None<Key>Tab:\n"
203                                                        "<FocusIn>:focusIn()\n"
204                                                        "<FocusOut>:focusOut()\n"
205                                                        "<Enter>:enter()\n"
206                                                        "<Leave>:leave()\n"));
207
208 #endif
209
210         XtAddEventHandler(new, 0, TRUE, EventHandler, (XtPointer) NULL);
211
212         ecw->externalClient.shell_ready = False;
213         ecw->externalClient.has_focus = False;
214
215         if (!error_handler_added) {
216                 error_handler_added = 1;
217                 error_old_handler = XSetErrorHandler(my_error_handler);
218         }
219 }
220
221 #ifdef TOOLTALK
222 static Tt_callback_action tt_callback(Tt_message m, Tt_pattern p)
223 {
224         ExternalClientWidget ecw = (ExternalClientWidget) tt_message_user(m, 0);
225
226         switch (tt_message_state(m)) {
227         case TT_FAILED:
228                 /* handle errors here */
229                 break;
230         case TT_HANDLED:
231                 ecw->externalClient.shell_name = tt_message_arg_val(m, 2);
232                 XtCallCallbackList((Widget) ecw,
233                                    ecw->externalClient.shell_ready_callback,
234                                    NULL);
235                 break;
236         }
237
238         tt_message_destroy(m);
239         return TT_CALLBACK_PROCESSED;
240 }
241
242 static void
243 send_tooltalk_handshake(ExternalClientWidget ecw, Window win, char *name)
244 {
245         Tt_message m = tt_message_create();
246
247         tt_message_op_set(m, "emacs-make-client-screen");
248         tt_message_scope_set(m, TT_SESSION);
249         tt_message_class_set(m, TT_REQUEST);
250         tt_message_arg_add(m, TT_IN, "string", name);
251         tt_message_iarg_add(m, TT_IN, "int", win);
252         tt_message_arg_add(m, TT_OUT, "string", NULL);
253         tt_message_user_set(m, 0, (void *)ecw);
254         tt_message_callback_add(m, tt_callback);
255         if (ecw->externalClient.emacs_procid) {
256                 tt_message_address_set(m, TT_HANDLER);
257                 tt_message_handler_set(m, ecw->externalClient.emacs_procid);
258         } else
259                 tt_message_address_set(m, TT_PROCEDURE);
260         tt_message_send(m);
261 }
262
263 #endif
264
265 static void
266 externalClientRealize(Widget w, XtValueMask * vm, XSetWindowAttributes * attrs)
267 {
268         ExternalClientWidget ecw = (ExternalClientWidget) w;
269
270 #ifdef EXTW_USES_MOTIF
271         (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
272 #else
273         (*coreWidgetClass->core_class.realize) (w, vm, attrs);
274 #endif
275
276 #ifdef TOOLTALK
277
278         /* Make sure that the server actually knows about this window id before
279          * telling Emacs about it.
280          */
281         if (ecw->externalClient.use_tooltalk) {
282                 XSync(XtDisplay(w), False);
283                 send_tooltalk_handshake(ecw, XtWindow(w), XtName(w));
284         }
285 #endif
286 }
287
288 /***********************************************************************/
289
290 /* window-to-widget list. */
291
292 struct ww_list {
293         Window win;
294         Widget wid;
295         struct ww_list *next;
296 };
297
298 struct ww_list ww_list[1];
299
300 static int add_ww(Window win, Widget wid)
301 {
302         struct ww_list *ww = (struct ww_list *)malloc(sizeof(struct ww_list));
303         if (!ww)
304                 return 0;
305         ww->win = win;
306         ww->wid = wid;
307         ww->next = ww_list->next;
308         ww_list->next = ww;
309         return 1;
310 }
311
312 static Widget remove_ww(Window win)
313 {
314         struct ww_list *w1, *w2;
315         Widget wid = 0;
316
317         for (w1 = ww_list, w2 = w1->next; w2; w1 = w2, w2 = w2->next)
318                 if (w2->win == win) {
319                         w1->next = w2->next;
320                         wid = w2->wid;
321                         free(w2);
322                         break;
323                 }
324         return wid;
325 }
326
327 /***********************************************************************/
328
329 /* stolen outright from Intrinsic.c */
330
331 static void ComputeWindowAttributes(widget, value_mask, values)
332 Widget widget;
333 XtValueMask *value_mask;
334 XSetWindowAttributes *values;
335 {
336         *value_mask = CWEventMask | CWColormap;
337         (*values).event_mask = XtBuildEventMask(widget);
338         (*values).colormap = widget->core.colormap;
339         if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
340                 *value_mask |= CWBackPixmap;
341                 (*values).background_pixmap = widget->core.background_pixmap;
342         } else {
343                 *value_mask |= CWBackPixel;
344                 (*values).background_pixel = widget->core.background_pixel;
345         }
346         if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
347                 *value_mask |= CWBorderPixmap;
348                 (*values).border_pixmap = widget->core.border_pixmap;
349         } else {
350                 *value_mask |= CWBorderPixel;
351                 (*values).border_pixel = widget->core.border_pixel;
352         }
353         if (widget->core.widget_class->core_class.expose == (XtExposeProc) NULL) {
354                 /* Try to avoid redisplay upon resize by making bit_gravity the same
355                    as the default win_gravity */
356                 *value_mask |= CWBitGravity;
357                 (*values).bit_gravity = NorthWestGravity;
358         }
359 }                               /* ComputeWindowAttributes */
360
361 static void end_connection(ExternalClientWidget w)
362 {
363         XSetWindowAttributes xswa;
364         XtValueMask mask;
365         Widget wid = (Widget) w;
366
367         w->externalClient.shell_ready = False;
368         XtRemoveEventHandler(wid, w->externalClient.event_mask,
369                              FALSE, MaskableEventHandler, (XtPointer) NULL);
370         ComputeWindowAttributes(wid, &mask, &xswa);
371         XChangeWindowAttributes(XtDisplay(wid), XtWindow(wid), mask, &xswa);
372         XClearArea(XtDisplay(wid), XtWindow(wid), 0, 0, 0, 0, True);
373 }
374
375 static int my_error_handler(Display * display, XErrorEvent * xev)
376 {
377         Widget wid;
378
379         if (xev->error_code != BadWindow)
380                 goto call_old;
381         wid = remove_ww(xev->resourceid);
382         if (wid) {
383                 end_connection((ExternalClientWidget) wid);
384                 return 0;
385         }
386
387       call_old:
388         return error_old_handler(display, xev);
389 }
390
391 static void
392 MaskableEventHandler(Widget wid, XtPointer closure, XEvent * event,
393                      Boolean * continue_to_dispatch)
394      /* closure and continue_to_dispatch unused */
395 {
396         ExternalClientWidget w = (ExternalClientWidget) wid;
397
398         if (w->externalClient.shell_ready) {
399                 if (event->type == KeyPress || event->type == KeyRelease ||
400                     event->type == ButtonPress || event->type == ButtonRelease
401                     || event->type == MotionNotify)
402                         event->xkey.subwindow = 0;
403 #ifdef EXTW_USES_MOTIF
404                 /* hackkkkkkkkkkkkkk!  Suppress CTRL-TAB, SHIFT-TAB, etc. so that
405                    Emacs doesn't attempt to interpret focus-change keystrokes. */
406                 if (event->type == KeyPress &&
407                     XLookupKeysym((XKeyEvent *) event, 0) == XK_Tab &&
408                     (event->xkey.state & ControlMask ||
409                      event->xkey.state & ShiftMask))
410                         return;
411 #endif
412                 event->xany.window = w->core.window;
413                 XSendEvent(XtDisplay(wid), w->externalClient.event_window,
414                            FALSE, 0, event);
415                 XSync(XtDisplay(wid), 0);       /* make sure that any BadWindow errors
416                                                    (meaning the server died) get handled
417                                                    before XSendEvent is called again. */
418
419         }
420 }
421
422 static void
423 EventHandler(Widget wid, XtPointer closure, XEvent * event,
424              Boolean * continue_to_dispatch)
425      /* closure and continue_to_dispatch unused */
426 {
427         ExternalClientWidget w = (ExternalClientWidget) wid;
428
429         if (w->core.window != event->xany.window) {
430                 XtAppErrorMsg(XtWidgetToApplicationContext(wid),
431                               "invalidWindow", "eventHandler",
432                               XtCXtToolkitError, "Event with wrong window",
433                               (String *) NULL, (Cardinal *) NULL);
434                 return;
435         }
436
437         if (event->type == ClientMessage &&
438             event->xclient.message_type == a_EXTW_NOTIFY &&
439             event->xclient.data.l[0] == extw_shell_send)
440                 switch (event->xclient.data.l[1]) {
441
442                 case extw_notify_qg:
443                         /* shell is alive again. */
444
445                         w->externalClient.dead_shell = False;
446                         break;
447
448                 case extw_notify_gm:
449                         {
450                                 XtWidgetGeometry xwg, xwg_return;
451                                 XtGeometryResult result;
452
453                                 extw_get_geometry_value(XtDisplay(wid),
454                                                         XtWindow(wid),
455                                                         a_EXTW_GEOMETRY_MANAGER,
456                                                         &xwg);
457                                 result =
458                                     XtMakeGeometryRequest(wid, &xwg,
459                                                           &xwg_return);
460
461                                 extw_send_geometry_value(XtDisplay(wid),
462                                                          XtWindow(wid),
463                                                          a_EXTW_GEOMETRY_MANAGER,
464                                                          extw_notify_gm,
465                                                          result ==
466                                                          XtGeometryAlmost ?
467                                                          &xwg_return : NULL,
468                                                          result);
469                                 break;
470                         }
471
472                 case extw_notify_init:
473                         w->externalClient.shell_ready = True;
474                         w->externalClient.event_window =
475                             event->xclient.data.l[2];
476                         w->externalClient.event_mask = event->xclient.data.l[3];
477                         add_ww(w->externalClient.event_window, (Widget) w);
478
479                         XtAddEventHandler(wid, w->externalClient.event_mask,
480                                           FALSE, MaskableEventHandler,
481                                           (XtPointer) NULL);
482 #ifdef EXTW_USES_MOTIF
483                         NOTIFY(w, extw_notify_init, EXTW_TYPE_MOTIF, 0, 0);
484 #else
485                         NOTIFY(w, extw_notify_init, EXTW_TYPE_XT, 0, 0);
486 #endif
487                         break;
488
489                 case extw_notify_end:
490                         end_connection(w);
491                         remove_ww(w->externalClient.event_window);
492                         break;
493
494                 case extw_notify_set_focus:
495 #ifdef EXTW_USES_MOTIF
496                         XmProcessTraversal(wid, XmTRAVERSE_CURRENT);
497 #else
498                         XtSetKeyboardFocus(wid, None);
499 #endif
500                         break;
501
502                 }
503 }
504
505 static void Destroy(wid)
506 Widget wid;
507 {
508         ExternalClientWidget w = (ExternalClientWidget) wid;
509
510         NOTIFY(w, extw_notify_end, 0, 0, 0);
511 }
512
513 static XtGeometryResult QueryGeometry(gw, request, reply)
514 Widget gw;
515 XtWidgetGeometry *request, *reply;
516 {
517         ExternalClientWidget w = (ExternalClientWidget) gw;
518         XEvent event;
519         unsigned long request_num;
520         Display *display = XtDisplay(gw);
521         XtWidgetGeometry req = *request;        /* don't modify caller's structure */
522
523         if (!XtIsRealized((Widget) w) || !w->externalClient.shell_ready)
524                 return XtGeometryYes;
525
526         if (w->externalClient.dead_shell == TRUE)
527                 /* The shell is sick. */
528                 return XtGeometryNo;
529
530         req.sibling = None;
531         req.request_mode &= ~CWSibling;
532         request_num = NextRequest(display);
533         extw_send_geometry_value(XtDisplay(gw), XtWindow(gw),
534                                  a_EXTW_QUERY_GEOMETRY, extw_notify_qg, &req,
535                                  0);
536
537         if (extw_wait_for_response(gw, &event, request_num, extw_notify_qg,
538                                    w->externalClient.shell_timeout)) {
539                 XtGeometryResult result =
540                     (XtGeometryResult) event.xclient.data.l[0];
541
542                 if (result == XtGeometryAlmost) {
543                         extw_get_geometry_value(XtDisplay(gw), XtWindow(gw),
544                                                 a_EXTW_QUERY_GEOMETRY, reply);
545                 }
546                 return result;
547         } else {
548                 w->externalClient.dead_shell = TRUE;    /* timed out; must be broken */
549                 return XtGeometryNo;
550         }
551 }
552
553 static void ExternalClientFocusIn(Widget w, XEvent * event, String * params,
554                                   Cardinal * num_params)
555 {
556         ExternalClientWidget ecw = (ExternalClientWidget) w;
557
558         if (event->xfocus.send_event && !ecw->externalClient.has_focus) {
559                 ecw->externalClient.has_focus = True;
560                 NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
561         }
562 #ifdef EXTW_USES_MOTIF
563         _XmPrimitiveFocusIn(w, event, params, num_params);
564 #endif
565 }
566
567 static void ExternalClientFocusOut(Widget w, XEvent * event, String * params,
568                                    Cardinal * num_params)
569 {
570         ExternalClientWidget ecw = (ExternalClientWidget) w;
571
572         if (event->xfocus.send_event && ecw->externalClient.has_focus) {
573                 ecw->externalClient.has_focus = False;
574                 NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
575         }
576 #ifdef EXTW_USES_MOTIF
577         _XmPrimitiveFocusOut(w, event, params, num_params);
578 #endif
579 }
580
581 static void ExternalClientEnter(Widget w, XEvent * event, String * params,
582                                 Cardinal * num_params)
583 {
584         ExternalClientWidget ecw = (ExternalClientWidget) w;
585
586         if (
587 #ifdef EXTW_USES_MOTIF
588                    _XmGetFocusPolicy(w) != XmEXPLICIT &&
589 #endif
590                    !ecw->externalClient.has_focus &&
591                    event->xcrossing.focus
592                    && event->xcrossing.detail != NotifyInferior) {
593                 ecw->externalClient.has_focus = True;
594                 NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
595         }
596 #ifdef EXTW_USES_MOTIF
597         _XmPrimitiveEnter(w, event, params, num_params);
598 #endif
599 }
600
601 static void ExternalClientLeave(Widget w, XEvent * event, String * params,
602                                 Cardinal * num_params)
603 {
604         ExternalClientWidget ecw = (ExternalClientWidget) w;
605
606         if (
607 #ifdef EXTW_USES_MOTIF
608                    _XmGetFocusPolicy(w) != XmEXPLICIT &&
609 #endif
610                    ecw->externalClient.has_focus &&
611                    event->xcrossing.focus
612                    && event->xcrossing.detail != NotifyInferior) {
613                 ecw->externalClient.has_focus = False;
614                 NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
615         }
616 #ifdef EXTW_USES_MOTIF
617         _XmPrimitiveLeave(w, event, params, num_params);
618 #endif
619 }