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, 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},
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}
100 static XtActionsRec actions[] = {
101 {"focusIn", ExternalClientFocusIn},
102 {"focusOut", ExternalClientFocusOut},
103 {"enter", ExternalClientEnter},
104 {"leave", ExternalClientLeave},
107 ExternalClientClassRec externalClientClassRec = {
111 #ifdef EXTW_USES_MOTIF
112 /* superclass */ (WidgetClass) &xmPrimitiveClassRec,
114 /* superclass */ (WidgetClass) &coreClassRec,
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,
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
147 /* query_geometry */ QueryGeometry,
148 /* display_accelerator*/ NULL,
151 #ifdef EXTW_USES_MOTIF
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 */
167 WidgetClass externalClientWidgetClass = (WidgetClass) &externalClientClassRec;
170 externalClientInitialize (Widget req, Widget new, ArgList args,
173 ExternalClientWidget ecw = (ExternalClientWidget) new;
174 static int error_handler_added = 0;
176 extw_initialize_atoms (XtDisplay (new));
177 extw_which_side = extw_client_send;
179 #ifdef EXTW_USES_MOTIF
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.
190 XtOverrideTranslations (new,
191 XtParseTranslationTable ("None<Key>Tab:\n"
192 "<FocusIn>:focusIn()\n"
193 "<FocusOut>:focusOut()\n"
195 "<Leave>:leave()\n"));
199 XtAddEventHandler (new, 0, TRUE, EventHandler, (XtPointer) NULL);
201 ecw->externalClient.shell_ready = False;
202 ecw->externalClient.has_focus = False;
204 if (!error_handler_added)
206 error_handler_added = 1;
207 error_old_handler = XSetErrorHandler (my_error_handler);
213 static Tt_callback_action
214 tt_callback(Tt_message m, Tt_pattern p)
216 ExternalClientWidget ecw = (ExternalClientWidget)tt_message_user (m, 0);
218 switch (tt_message_state(m))
221 /* handle errors here */
224 ecw->externalClient.shell_name = tt_message_arg_val (m, 2);
225 XtCallCallbackList ((Widget) ecw,
226 ecw->externalClient.shell_ready_callback, NULL);
230 tt_message_destroy (m);
231 return TT_CALLBACK_PROCESSED;
235 send_tooltalk_handshake (ExternalClientWidget ecw, Window win, char *name)
237 Tt_message m = tt_message_create ();
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)
249 tt_message_address_set (m, TT_HANDLER);
250 tt_message_handler_set (m, ecw->externalClient.emacs_procid);
253 tt_message_address_set (m, TT_PROCEDURE);
261 externalClientRealize (Widget w, XtValueMask *vm, XSetWindowAttributes *attrs)
263 ExternalClientWidget ecw = (ExternalClientWidget)w;
265 #ifdef EXTW_USES_MOTIF
266 (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
268 (*coreWidgetClass->core_class.realize) (w, vm, attrs);
273 /* Make sure that the server actually knows about this window id before
274 * telling Emacs about it.
276 if (ecw->externalClient.use_tooltalk)
278 XSync (XtDisplay (w), False);
279 send_tooltalk_handshake (ecw, XtWindow (w), XtName (w));
285 /***********************************************************************/
287 /* window-to-widget list. */
293 struct ww_list *next;
296 struct ww_list ww_list[1];
299 add_ww (Window win, Widget wid)
301 struct ww_list *ww = (struct ww_list *) malloc (sizeof (struct
307 ww->next = ww_list->next;
313 remove_ww (Window win)
315 struct ww_list *w1, *w2;
318 for (w1=ww_list, w2=w1->next; w2; w1=w2, w2=w2->next)
329 /***********************************************************************/
331 /* stolen outright from Intrinsic.c */
333 static void ComputeWindowAttributes(widget,value_mask,values)
335 XtValueMask *value_mask;
336 XSetWindowAttributes *values;
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;
345 *value_mask |= CWBackPixel;
346 (*values).background_pixel = widget->core.background_pixel;
348 if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
349 *value_mask |= CWBorderPixmap;
350 (*values).border_pixmap = widget->core.border_pixmap;
352 *value_mask |= CWBorderPixel;
353 (*values).border_pixel = widget->core.border_pixel;
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;
361 } /* ComputeWindowAttributes */
364 end_connection (ExternalClientWidget w)
366 XSetWindowAttributes xswa;
368 Widget wid = (Widget) w;
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);
379 my_error_handler (Display *display, XErrorEvent *xev)
383 if (xev->error_code != BadWindow)
385 wid = remove_ww (xev->resourceid);
388 end_connection ((ExternalClientWidget) wid);
393 return error_old_handler (display, xev);
397 MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
398 Boolean *continue_to_dispatch)
399 /* closure and continue_to_dispatch unused */
401 ExternalClientWidget w = (ExternalClientWidget) wid;
403 if (w->externalClient.shell_ready)
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))
418 event->xany.window = w->core.window;
419 XSendEvent (XtDisplay (wid), w->externalClient.event_window, FALSE, 0,
421 XSync (XtDisplay (wid), 0); /* make sure that any BadWindow errors
422 (meaning the server died) get handled
423 before XSendEvent is called again. */
429 EventHandler (Widget wid, XtPointer closure, XEvent *event,
430 Boolean *continue_to_dispatch)
431 /* closure and continue_to_dispatch unused */
433 ExternalClientWidget w = (ExternalClientWidget) wid;
435 if (w->core.window != event->xany.window)
437 XtAppErrorMsg (XtWidgetToApplicationContext (wid),
438 "invalidWindow","eventHandler",XtCXtToolkitError,
439 "Event with wrong window",
440 (String *)NULL, (Cardinal *)NULL);
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])
451 /* shell is alive again. */
453 w->externalClient.dead_shell = False;
458 XtWidgetGeometry xwg, xwg_return;
459 XtGeometryResult result;
461 extw_get_geometry_value (XtDisplay (wid), XtWindow (wid),
462 a_EXTW_GEOMETRY_MANAGER, &xwg);
463 result = XtMakeGeometryRequest (wid, &xwg, &xwg_return);
465 extw_send_geometry_value (XtDisplay (wid), XtWindow (wid),
466 a_EXTW_GEOMETRY_MANAGER, extw_notify_gm,
467 result == XtGeometryAlmost ? &xwg_return :
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);
478 XtAddEventHandler (wid, w->externalClient.event_mask,
479 FALSE, MaskableEventHandler, (XtPointer) NULL);
480 #ifdef EXTW_USES_MOTIF
481 NOTIFY (w, extw_notify_init,
485 NOTIFY (w, extw_notify_init,
491 case extw_notify_end:
493 remove_ww (w->externalClient.event_window);
496 case extw_notify_set_focus:
497 #ifdef EXTW_USES_MOTIF
498 XmProcessTraversal (wid, XmTRAVERSE_CURRENT);
500 XtSetKeyboardFocus (wid, None);
507 static void Destroy(wid)
510 ExternalClientWidget w = (ExternalClientWidget)wid;
512 NOTIFY(w, extw_notify_end, 0, 0, 0);
515 static XtGeometryResult QueryGeometry(gw, request, reply)
517 XtWidgetGeometry *request, *reply;
519 ExternalClientWidget w = (ExternalClientWidget)gw;
521 unsigned long request_num;
522 Display *display = XtDisplay(gw);
523 XtWidgetGeometry req = *request; /* don't modify caller's structure */
525 if (!XtIsRealized((Widget)w) || !w->externalClient.shell_ready)
526 return XtGeometryYes;
528 if (w->externalClient.dead_shell == TRUE)
529 /* The shell is sick. */
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);
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];
542 if (result == XtGeometryAlmost) {
543 extw_get_geometry_value(XtDisplay(gw), XtWindow(gw),
544 a_EXTW_QUERY_GEOMETRY, reply);
548 w->externalClient.dead_shell = TRUE; /* timed out; must be broken */
553 static void ExternalClientFocusIn (Widget w, XEvent *event, String *params,
554 Cardinal *num_params)
556 ExternalClientWidget ecw = (ExternalClientWidget) w;
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);
562 #ifdef EXTW_USES_MOTIF
563 _XmPrimitiveFocusIn (w, event, params, num_params);
567 static void ExternalClientFocusOut (Widget w, XEvent *event, String *params,
568 Cardinal *num_params)
570 ExternalClientWidget ecw = (ExternalClientWidget) w;
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);
576 #ifdef EXTW_USES_MOTIF
577 _XmPrimitiveFocusOut(w, event, params, num_params);
581 static void ExternalClientEnter (Widget w, XEvent *event, String *params,
582 Cardinal *num_params)
584 ExternalClientWidget ecw = (ExternalClientWidget) w;
587 #ifdef EXTW_USES_MOTIF
588 _XmGetFocusPolicy (w) != XmEXPLICIT &&
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);
595 #ifdef EXTW_USES_MOTIF
596 _XmPrimitiveEnter (w, event, params, num_params);
600 static void ExternalClientLeave (Widget w, XEvent *event, String *params,
601 Cardinal *num_params)
603 ExternalClientWidget ecw = (ExternalClientWidget) w;
606 #ifdef EXTW_USES_MOTIF
607 _XmGetFocusPolicy (w) != XmEXPLICIT &&
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);
614 #ifdef EXTW_USES_MOTIF
615 _XmPrimitiveLeave (w, event, params, num_params);