XEmacs 21.2-b1
[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_PATH
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, XtRInt, sizeof(int),
81       offset(shell_timeout), XtRImmediate,(XtPointer)DEFAULT_WM_TIMEOUT},
82   { XtNdeadShell, XtCDeadShell, XtRBoolean, sizeof(Boolean),
83       offset(dead_shell), XtRImmediate, (XtPointer)False},
84 #ifdef EXTW_USES_MOTIF
85   { XmNnavigationType, XmCNavigationType, XmRNavigationType,
86       sizeof(XmNavigationType), XtOffset(ExternalClientWidget,
87       primitive.navigation_type), XtRImmediate,
88       (XtPointer)XmTAB_GROUP},
89 #endif
90   { XtNemacsProcID, XtCEmacsProcID, XtRString, sizeof(String),
91       offset(emacs_procid), XtRImmediate, (XtPointer)NULL},
92   { XtNshellReadyCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
93       offset(shell_ready_callback), XtRImmediate, (XtPointer)NULL},
94   { XtNshellName, XtCShellName, XtRString, sizeof(String),
95       offset(shell_name), XtRImmediate, (XtPointer)NULL},
96   { XtNuseToolTalk, XtCUseToolTalk, XtRBoolean, sizeof(Boolean),
97       offset(use_tooltalk), XtRImmediate, (XtPointer)False}
98 };
99
100 static XtActionsRec actions[] = {
101   {"focusIn",   ExternalClientFocusIn},
102   {"focusOut",  ExternalClientFocusOut},
103   {"enter",     ExternalClientEnter},
104   {"leave",     ExternalClientLeave},
105 };
106
107 ExternalClientClassRec externalClientClassRec = {
108     { /*
109        *        core_class fields
110        */
111 #ifdef EXTW_USES_MOTIF
112     /* superclass         */    (WidgetClass) &xmPrimitiveClassRec,
113 #else
114     /* superclass         */    (WidgetClass) &coreClassRec,
115 #endif
116     /* class_name         */    "ExternalClient",
117     /* size               */    sizeof(ExternalClientRec),
118     /* Class Initializer  */    NULL,
119     /* class_part_initialize*/  NULL, /* XtInheritClassPartInitialize, */
120     /* Class init'ed ?    */    FALSE,
121     /* initialize         */    externalClientInitialize,
122     /* initialize_notify  */    NULL,
123     /* realize            */    externalClientRealize, 
124     /* actions            */    actions,
125     /* num_actions        */    XtNumber (actions),
126     /* resources          */    resources,
127     /* resource_count     */    XtNumber (resources),
128     /* xrm_class          */    NULLQUARK,
129     /* compress_motion    */    FALSE,
130     /* compress_exposure  */    TRUE,
131     /* compress_enterleave*/    FALSE,
132     /* visible_interest   */    TRUE,
133     /* destroy            */    Destroy, /* XtInheritDestroy, */
134     /* resize             */    XtInheritResize,
135     /* expose             */    NULL,
136     /* set_values         */    NULL, /* XtInheritSetValues, */
137     /* set_values_hook    */    NULL,                   
138     /* set_values_almost  */    XtInheritSetValuesAlmost,  
139     /* get_values_hook    */    NULL,                   
140     /* accept_focus       */    NULL,
141     /* intrinsics version */    XtVersion,
142     /* callback offsets   */    NULL,
143     /* tm_table           */    "", /* MUST NOT BE NULL or
144                                        XtInheritTranslations in Motif!!!!!
145                                        Otherwise keyboard focus translations
146                                        will not work. */
147     /* query_geometry     */    QueryGeometry,
148     /* display_accelerator*/    NULL,
149     /* extension          */    NULL
150   },
151 #ifdef EXTW_USES_MOTIF
152   {
153     XmInheritBorderHighlight,/* Primitive border_highlight */
154     XmInheritBorderHighlight,/* Primitive border_unhighlight */
155     XtInheritTranslations,   /* translations */
156     NULL,                    /* arm_and_activate */
157     NULL,                    /* get resources */
158     0,                       /* num get_resources */
159     NULL,                    /* extension */
160   },
161 #endif
162   {
163     0
164   }
165 };
166
167 WidgetClass externalClientWidgetClass = (WidgetClass) &externalClientClassRec;
168
169 static void
170 externalClientInitialize (Widget req, Widget new, ArgList args,
171                           Cardinal *num_args)
172 {
173   ExternalClientWidget ecw = (ExternalClientWidget) new;
174   static int error_handler_added = 0;
175
176   extw_initialize_atoms (XtDisplay (new));
177   extw_which_side = extw_client_send;
178
179 #ifdef EXTW_USES_MOTIF
180
181   /* yes I know this is horrible.  However, the XmPrimitive class adds
182      the Tab translation in its initialization routine, so we have to
183      override it here.  This is all the fault of Xt, which doesn't
184      provide a proper inheritance mechanism for translations.
185
186      -- BPW
187
188   */
189     
190   XtOverrideTranslations (new,
191                           XtParseTranslationTable ("None<Key>Tab:\n"
192                                                    "<FocusIn>:focusIn()\n"
193                                                    "<FocusOut>:focusOut()\n"
194                                                    "<Enter>:enter()\n"
195                                                    "<Leave>:leave()\n"));
196
197 #endif
198   
199   XtAddEventHandler (new, 0, TRUE, EventHandler, (XtPointer) NULL);
200
201   ecw->externalClient.shell_ready = False;
202   ecw->externalClient.has_focus = False;
203
204   if (!error_handler_added)
205     {
206       error_handler_added = 1;
207       error_old_handler = XSetErrorHandler (my_error_handler);
208     }
209 }
210
211
212 #ifdef TOOLTALK
213 static Tt_callback_action
214 tt_callback(Tt_message m, Tt_pattern p)
215 {
216   ExternalClientWidget ecw = (ExternalClientWidget)tt_message_user (m, 0);
217   
218   switch (tt_message_state(m))
219     {
220     case TT_FAILED:
221       /* handle errors here */
222       break;
223     case TT_HANDLED:
224       ecw->externalClient.shell_name = tt_message_arg_val (m, 2);
225       XtCallCallbackList ((Widget) ecw,
226                           ecw->externalClient.shell_ready_callback, NULL);
227       break;
228     }
229   
230   tt_message_destroy (m);
231   return TT_CALLBACK_PROCESSED;
232 }
233
234 static void
235 send_tooltalk_handshake (ExternalClientWidget ecw, Window win, char *name)
236 {
237   Tt_message m = tt_message_create ();
238
239   tt_message_op_set (m, "emacs-make-client-screen");
240   tt_message_scope_set (m, TT_SESSION);
241   tt_message_class_set (m, TT_REQUEST);
242   tt_message_arg_add (m, TT_IN, "string", name);
243   tt_message_iarg_add (m, TT_IN, "int", win);
244   tt_message_arg_add (m, TT_OUT, "string", NULL); 
245   tt_message_user_set (m, 0, (void *)ecw);
246   tt_message_callback_add (m, tt_callback);
247   if (ecw->externalClient.emacs_procid)
248     {
249       tt_message_address_set (m, TT_HANDLER);
250       tt_message_handler_set (m, ecw->externalClient.emacs_procid);
251   }
252   else
253     tt_message_address_set (m, TT_PROCEDURE);
254   tt_message_send (m);
255 }
256
257 #endif
258
259
260 static void
261 externalClientRealize (Widget w, XtValueMask *vm, XSetWindowAttributes *attrs)
262 {
263   ExternalClientWidget ecw = (ExternalClientWidget)w;
264   
265 #ifdef EXTW_USES_MOTIF  
266   (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
267 #else
268   (*coreWidgetClass->core_class.realize) (w, vm, attrs);
269 #endif
270
271 #ifdef TOOLTALK
272
273   /* Make sure that the server actually knows about this window id before
274    * telling Emacs about it.
275    */
276   if (ecw->externalClient.use_tooltalk)
277     {
278       XSync (XtDisplay (w), False);
279       send_tooltalk_handshake (ecw, XtWindow (w), XtName (w));
280     }
281 #endif  
282 }
283
284
285 /***********************************************************************/
286
287 /* window-to-widget list. */
288
289 struct ww_list
290 {
291   Window win;
292   Widget wid;
293   struct ww_list *next;
294 };
295
296 struct ww_list ww_list[1];
297
298 static int
299 add_ww (Window win, Widget wid)
300 {
301   struct ww_list *ww = (struct ww_list *) malloc (sizeof (struct
302                                                           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
313 remove_ww (Window win)
314 {
315   struct ww_list *w1, *w2;
316   Widget wid = 0;
317   
318   for (w1=ww_list, w2=w1->next; w2; w1=w2, w2=w2->next)
319     if (w2->win == win)
320       {
321         w1->next = w2->next;
322         wid = w2->wid;
323         free (w2);
324         break;
325       }
326   return wid;
327 }
328
329 /***********************************************************************/
330
331 /* stolen outright from Intrinsic.c */
332
333 static void ComputeWindowAttributes(widget,value_mask,values)
334      Widget              widget;
335      XtValueMask                 *value_mask;
336      XSetWindowAttributes *values;
337 {
338   *value_mask = CWEventMask | CWColormap;
339   (*values).event_mask = XtBuildEventMask(widget);
340   (*values).colormap = widget->core.colormap;
341   if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
342     *value_mask |= CWBackPixmap;
343     (*values).background_pixmap = widget->core.background_pixmap;
344   } else {
345     *value_mask |= CWBackPixel;
346     (*values).background_pixel = widget->core.background_pixel;
347   }
348   if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
349     *value_mask |= CWBorderPixmap;
350     (*values).border_pixmap = widget->core.border_pixmap;
351   } else {
352     *value_mask |= CWBorderPixel;
353     (*values).border_pixel = widget->core.border_pixel;
354   }
355   if (widget->core.widget_class->core_class.expose == (XtExposeProc) NULL) {
356     /* Try to avoid redisplay upon resize by making bit_gravity the same
357        as the default win_gravity */
358     *value_mask |= CWBitGravity;
359     (*values).bit_gravity = NorthWestGravity;
360   }
361 } /* ComputeWindowAttributes */
362
363 static void
364 end_connection (ExternalClientWidget w)
365 {
366   XSetWindowAttributes xswa;
367   XtValueMask mask;
368   Widget wid = (Widget) w;
369   
370   w->externalClient.shell_ready = False;
371   XtRemoveEventHandler (wid, w->externalClient.event_mask,
372                         FALSE, MaskableEventHandler, (XtPointer) NULL);
373   ComputeWindowAttributes (wid, &mask, &xswa);
374   XChangeWindowAttributes (XtDisplay (wid), XtWindow (wid), mask, &xswa);
375   XClearArea (XtDisplay (wid), XtWindow (wid), 0, 0, 0, 0, True);
376 }
377
378 static int
379 my_error_handler (Display *display, XErrorEvent *xev)
380 {
381   Widget wid;
382   
383   if (xev->error_code != BadWindow)
384     goto call_old;
385   wid = remove_ww (xev->resourceid);
386   if (wid)
387     {
388       end_connection ((ExternalClientWidget) wid);
389       return 0;
390     }
391   
392  call_old:
393   return error_old_handler (display, xev);
394 }
395
396 static void
397 MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
398                       Boolean *continue_to_dispatch)
399      /* closure and continue_to_dispatch unused */
400 {
401   ExternalClientWidget w = (ExternalClientWidget) wid;
402   
403   if (w->externalClient.shell_ready)
404     {
405       if (event->type == KeyPress || event->type == KeyRelease ||
406           event->type == ButtonPress || event->type == ButtonRelease ||
407           event->type == MotionNotify)
408         event->xkey.subwindow = 0;
409 #ifdef EXTW_USES_MOTIF
410       /* hackkkkkkkkkkkkkk!  Suppress CTRL-TAB, SHIFT-TAB, etc. so that
411          Emacs doesn't attempt to interpret focus-change keystrokes. */
412       if (event->type == KeyPress &&
413           XLookupKeysym ((XKeyEvent *) event, 0) == XK_Tab &&
414           (event->xkey.state & ControlMask ||
415            event->xkey.state & ShiftMask))
416         return;
417 #endif
418       event->xany.window = w->core.window;
419       XSendEvent (XtDisplay (wid), w->externalClient.event_window, FALSE, 0,
420                   event);
421       XSync (XtDisplay (wid), 0); /* make sure that any BadWindow errors
422                                      (meaning the server died) get handled
423                                      before XSendEvent is called again. */
424       
425     }
426 }
427
428 static void
429 EventHandler (Widget wid, XtPointer closure, XEvent *event,
430               Boolean *continue_to_dispatch)
431      /* closure and continue_to_dispatch unused */
432 {
433   ExternalClientWidget w = (ExternalClientWidget) wid;
434   
435   if (w->core.window != event->xany.window)
436     {
437       XtAppErrorMsg (XtWidgetToApplicationContext (wid),
438                      "invalidWindow","eventHandler",XtCXtToolkitError,
439                      "Event with wrong window",
440                      (String *)NULL, (Cardinal *)NULL);
441       return;
442     }
443   
444   if (event->type == ClientMessage &&
445       event->xclient.message_type == a_EXTW_NOTIFY &&
446       event->xclient.data.l[0] == extw_shell_send)
447     switch (event->xclient.data.l[1])
448       {
449         
450       case extw_notify_qg:
451         /* shell is alive again. */
452         
453         w->externalClient.dead_shell = False;
454         break;
455         
456       case extw_notify_gm:
457         {
458           XtWidgetGeometry xwg, xwg_return;
459           XtGeometryResult result;
460           
461           extw_get_geometry_value (XtDisplay (wid), XtWindow (wid),
462                                    a_EXTW_GEOMETRY_MANAGER, &xwg);
463           result = XtMakeGeometryRequest (wid, &xwg, &xwg_return);
464           
465           extw_send_geometry_value (XtDisplay (wid), XtWindow (wid),
466                                     a_EXTW_GEOMETRY_MANAGER, extw_notify_gm,
467                                     result == XtGeometryAlmost ? &xwg_return :
468                                     NULL, result);
469           break;
470         }
471         
472       case extw_notify_init:
473         w->externalClient.shell_ready = True;
474         w->externalClient.event_window = event->xclient.data.l[2];
475         w->externalClient.event_mask = event->xclient.data.l[3];
476         add_ww (w->externalClient.event_window, (Widget) w);
477         
478         XtAddEventHandler (wid, w->externalClient.event_mask,
479                            FALSE, MaskableEventHandler, (XtPointer) NULL);
480 #ifdef EXTW_USES_MOTIF
481         NOTIFY (w, extw_notify_init,
482                 EXTW_TYPE_MOTIF,
483                 0, 0);
484 #else
485         NOTIFY (w, extw_notify_init,
486                 EXTW_TYPE_XT,
487                 0, 0);
488 #endif
489         break;
490         
491       case extw_notify_end:
492         end_connection (w);
493         remove_ww (w->externalClient.event_window);
494         break;
495         
496       case extw_notify_set_focus:
497 #ifdef EXTW_USES_MOTIF
498         XmProcessTraversal (wid, XmTRAVERSE_CURRENT);
499 #else
500         XtSetKeyboardFocus (wid, None);
501 #endif
502         break;
503         
504       }
505 }
506
507 static void Destroy(wid)
508      Widget wid;
509 {
510   ExternalClientWidget w = (ExternalClientWidget)wid;
511   
512   NOTIFY(w, extw_notify_end, 0, 0, 0);
513 }
514
515 static XtGeometryResult QueryGeometry(gw, request, reply)
516      Widget gw;
517      XtWidgetGeometry *request, *reply;
518 {
519   ExternalClientWidget w = (ExternalClientWidget)gw;
520   XEvent event;
521   unsigned long request_num;
522   Display *display = XtDisplay(gw);
523   XtWidgetGeometry req = *request; /* don't modify caller's structure */
524   
525   if (!XtIsRealized((Widget)w) || !w->externalClient.shell_ready)
526     return XtGeometryYes;
527   
528   if (w->externalClient.dead_shell == TRUE)
529     /* The shell is sick. */
530     return XtGeometryNo;
531   
532   req.sibling = None;
533   req.request_mode &= ~CWSibling;
534   request_num = NextRequest(display);
535   extw_send_geometry_value(XtDisplay(gw), XtWindow(gw), a_EXTW_QUERY_GEOMETRY,
536                            extw_notify_qg, &req, 0);
537
538   if (extw_wait_for_response(gw, &event, request_num, extw_notify_qg,
539                              w->externalClient.shell_timeout)) {
540     XtGeometryResult result = (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 && event->xcrossing.detail != NotifyInferior) {
592     ecw->externalClient.has_focus = True;
593     NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
594   }
595 #ifdef EXTW_USES_MOTIF
596   _XmPrimitiveEnter (w, event, params, num_params);
597 #endif
598 }
599
600 static void ExternalClientLeave (Widget w, XEvent *event, String *params,
601                               Cardinal *num_params)
602 {
603   ExternalClientWidget ecw = (ExternalClientWidget) w;
604   
605   if (
606 #ifdef EXTW_USES_MOTIF
607       _XmGetFocusPolicy (w) != XmEXPLICIT &&
608 #endif
609       ecw->externalClient.has_focus &&
610       event->xcrossing.focus && event->xcrossing.detail != NotifyInferior) {
611     ecw->externalClient.has_focus = False;
612     NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
613   }
614 #ifdef EXTW_USES_MOTIF
615   _XmPrimitiveLeave (w, event, params, num_params);
616 #endif
617 }