1 /* External client widget.
2 Copyright (C) 1993, 1994 Sun Microsystems, Inc.
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.
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.
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. */
19 /* Synched up with: Not in FSF. */
21 /* Written by Ben Wing, September 1993. */
27 #ifndef EXTERNAL_WIDGET
28 ERROR! This ought not be getting compiled if EXTERNAL_WIDGET is undefined
36 #ifdef EXTW_USES_MOTIF
38 # include <Xm/PrimitiveP.h>
39 # include <X11/keysym.h>
41 # include "xintrinsicp.h"
42 # include <X11/StringDefs.h>
45 #include "ExternalClientP.h"
52 /* This is the client widget, used to communicate with an ExternalShell
55 #define NOTIFY(w, type, l0, l1, l2) \
56 extw_send_notify_3(XtDisplay((Widget)(w)), XtWindow((Widget)(w)),\
59 static void externalClientInitialize (Widget req, Widget new, ArgList 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 *,
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 *);
75 static int my_error_handler(Display *display, XErrorEvent *xev);
76 static int (*error_old_handler)(Display *, XErrorEvent *);
78 static XtResource resources[] = {
79 #define offset(field) XtOffset(ExternalClientWidget, externalClient.field)
80 { XtNshellTimeout, XtCShellTimeout,
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 },
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 }
106 static XtActionsRec actions[] = {
107 {"focusIn", ExternalClientFocusIn},
108 {"focusOut", ExternalClientFocusOut},
109 {"enter", ExternalClientEnter},
110 {"leave", ExternalClientLeave},
113 ExternalClientClassRec externalClientClassRec = {
117 #ifdef EXTW_USES_MOTIF
118 /* superclass */ (WidgetClass) &xmPrimitiveClassRec,
120 /* superclass */ (WidgetClass) &coreClassRec,
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,
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
153 /* query_geometry */ QueryGeometry,
154 /* display_accelerator*/ NULL,
157 #ifdef EXTW_USES_MOTIF
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 */
173 WidgetClass externalClientWidgetClass = (WidgetClass) &externalClientClassRec;
176 externalClientInitialize (Widget req, Widget new, ArgList args,
179 ExternalClientWidget ecw = (ExternalClientWidget) new;
180 static int error_handler_added = 0;
182 extw_initialize_atoms (XtDisplay (new));
183 extw_which_side = extw_client_send;
185 #ifdef EXTW_USES_MOTIF
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.
196 XtOverrideTranslations (new,
197 XtParseTranslationTable ("None<Key>Tab:\n"
198 "<FocusIn>:focusIn()\n"
199 "<FocusOut>:focusOut()\n"
201 "<Leave>:leave()\n"));
205 XtAddEventHandler (new, 0, TRUE, EventHandler, (XtPointer) NULL);
207 ecw->externalClient.shell_ready = False;
208 ecw->externalClient.has_focus = False;
210 if (!error_handler_added)
212 error_handler_added = 1;
213 error_old_handler = XSetErrorHandler (my_error_handler);
219 static Tt_callback_action
220 tt_callback(Tt_message m, Tt_pattern p)
222 ExternalClientWidget ecw = (ExternalClientWidget)tt_message_user (m, 0);
224 switch (tt_message_state(m))
227 /* handle errors here */
230 ecw->externalClient.shell_name = tt_message_arg_val (m, 2);
231 XtCallCallbackList ((Widget) ecw,
232 ecw->externalClient.shell_ready_callback, NULL);
236 tt_message_destroy (m);
237 return TT_CALLBACK_PROCESSED;
241 send_tooltalk_handshake (ExternalClientWidget ecw, Window win, char *name)
243 Tt_message m = tt_message_create ();
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)
255 tt_message_address_set (m, TT_HANDLER);
256 tt_message_handler_set (m, ecw->externalClient.emacs_procid);
259 tt_message_address_set (m, TT_PROCEDURE);
267 externalClientRealize (Widget w, XtValueMask *vm, XSetWindowAttributes *attrs)
269 ExternalClientWidget ecw = (ExternalClientWidget)w;
271 #ifdef EXTW_USES_MOTIF
272 (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
274 (*coreWidgetClass->core_class.realize) (w, vm, attrs);
279 /* Make sure that the server actually knows about this window id before
280 * telling Emacs about it.
282 if (ecw->externalClient.use_tooltalk)
284 XSync (XtDisplay (w), False);
285 send_tooltalk_handshake (ecw, XtWindow (w), XtName (w));
291 /***********************************************************************/
293 /* window-to-widget list. */
299 struct ww_list *next;
302 struct ww_list ww_list[1];
305 add_ww (Window win, Widget wid)
307 struct ww_list *ww = (struct ww_list *) malloc (sizeof (struct
313 ww->next = ww_list->next;
319 remove_ww (Window win)
321 struct ww_list *w1, *w2;
324 for (w1=ww_list, w2=w1->next; w2; w1=w2, w2=w2->next)
335 /***********************************************************************/
337 /* stolen outright from Intrinsic.c */
339 static void ComputeWindowAttributes(widget,value_mask,values)
341 XtValueMask *value_mask;
342 XSetWindowAttributes *values;
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;
351 *value_mask |= CWBackPixel;
352 (*values).background_pixel = widget->core.background_pixel;
354 if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
355 *value_mask |= CWBorderPixmap;
356 (*values).border_pixmap = widget->core.border_pixmap;
358 *value_mask |= CWBorderPixel;
359 (*values).border_pixel = widget->core.border_pixel;
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;
367 } /* ComputeWindowAttributes */
370 end_connection (ExternalClientWidget w)
372 XSetWindowAttributes xswa;
374 Widget wid = (Widget) w;
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);
385 my_error_handler (Display *display, XErrorEvent *xev)
389 if (xev->error_code != BadWindow)
391 wid = remove_ww (xev->resourceid);
394 end_connection ((ExternalClientWidget) wid);
399 return error_old_handler (display, xev);
403 MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
404 Boolean *continue_to_dispatch)
405 /* closure and continue_to_dispatch unused */
407 ExternalClientWidget w = (ExternalClientWidget) wid;
409 if (w->externalClient.shell_ready)
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))
424 event->xany.window = w->core.window;
425 XSendEvent (XtDisplay (wid), w->externalClient.event_window, FALSE, 0,
427 XSync (XtDisplay (wid), 0); /* make sure that any BadWindow errors
428 (meaning the server died) get handled
429 before XSendEvent is called again. */
435 EventHandler (Widget wid, XtPointer closure, XEvent *event,
436 Boolean *continue_to_dispatch)
437 /* closure and continue_to_dispatch unused */
439 ExternalClientWidget w = (ExternalClientWidget) wid;
441 if (w->core.window != event->xany.window)
443 XtAppErrorMsg (XtWidgetToApplicationContext (wid),
444 "invalidWindow","eventHandler",XtCXtToolkitError,
445 "Event with wrong window",
446 (String *)NULL, (Cardinal *)NULL);
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])
457 /* shell is alive again. */
459 w->externalClient.dead_shell = False;
464 XtWidgetGeometry xwg, xwg_return;
465 XtGeometryResult result;
467 extw_get_geometry_value (XtDisplay (wid), XtWindow (wid),
468 a_EXTW_GEOMETRY_MANAGER, &xwg);
469 result = XtMakeGeometryRequest (wid, &xwg, &xwg_return);
471 extw_send_geometry_value (XtDisplay (wid), XtWindow (wid),
472 a_EXTW_GEOMETRY_MANAGER, extw_notify_gm,
473 result == XtGeometryAlmost ? &xwg_return :
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);
484 XtAddEventHandler (wid, w->externalClient.event_mask,
485 FALSE, MaskableEventHandler, (XtPointer) NULL);
486 #ifdef EXTW_USES_MOTIF
487 NOTIFY (w, extw_notify_init,
491 NOTIFY (w, extw_notify_init,
497 case extw_notify_end:
499 remove_ww (w->externalClient.event_window);
502 case extw_notify_set_focus:
503 #ifdef EXTW_USES_MOTIF
504 XmProcessTraversal (wid, XmTRAVERSE_CURRENT);
506 XtSetKeyboardFocus (wid, None);
513 static void Destroy(wid)
516 ExternalClientWidget w = (ExternalClientWidget)wid;
518 NOTIFY(w, extw_notify_end, 0, 0, 0);
521 static XtGeometryResult QueryGeometry(gw, request, reply)
523 XtWidgetGeometry *request, *reply;
525 ExternalClientWidget w = (ExternalClientWidget)gw;
527 unsigned long request_num;
528 Display *display = XtDisplay(gw);
529 XtWidgetGeometry req = *request; /* don't modify caller's structure */
531 if (!XtIsRealized((Widget)w) || !w->externalClient.shell_ready)
532 return XtGeometryYes;
534 if (w->externalClient.dead_shell == TRUE)
535 /* The shell is sick. */
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);
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];
548 if (result == XtGeometryAlmost) {
549 extw_get_geometry_value(XtDisplay(gw), XtWindow(gw),
550 a_EXTW_QUERY_GEOMETRY, reply);
554 w->externalClient.dead_shell = TRUE; /* timed out; must be broken */
559 static void ExternalClientFocusIn (Widget w, XEvent *event, String *params,
560 Cardinal *num_params)
562 ExternalClientWidget ecw = (ExternalClientWidget) w;
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);
568 #ifdef EXTW_USES_MOTIF
569 _XmPrimitiveFocusIn (w, event, params, num_params);
573 static void ExternalClientFocusOut (Widget w, XEvent *event, String *params,
574 Cardinal *num_params)
576 ExternalClientWidget ecw = (ExternalClientWidget) w;
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);
582 #ifdef EXTW_USES_MOTIF
583 _XmPrimitiveFocusOut(w, event, params, num_params);
587 static void ExternalClientEnter (Widget w, XEvent *event, String *params,
588 Cardinal *num_params)
590 ExternalClientWidget ecw = (ExternalClientWidget) w;
593 #ifdef EXTW_USES_MOTIF
594 _XmGetFocusPolicy (w) != XmEXPLICIT &&
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);
601 #ifdef EXTW_USES_MOTIF
602 _XmPrimitiveEnter (w, event, params, num_params);
606 static void ExternalClientLeave (Widget w, XEvent *event, String *params,
607 Cardinal *num_params)
609 ExternalClientWidget ecw = (ExternalClientWidget) w;
612 #ifdef EXTW_USES_MOTIF
613 _XmGetFocusPolicy (w) != XmEXPLICIT &&
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);
620 #ifdef EXTW_USES_MOTIF
621 _XmPrimitiveLeave (w, event, params, num_params);