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