--- /dev/null
+/* External client widget.
+ Copyright (C) 1993, 1994 Sun Microsystems, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+/* Synched up with: Not in FSF. */
+
+/* Written by Ben Wing, September 1993. */
+
+#ifdef emacs
+
+#include <config.h>
+
+#ifndef EXTERNAL_WIDGET
+ERROR! This ought not be getting compiled if EXTERNAL_WIDGET is undefined
+#endif
+
+#endif /* emacs */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef EXTW_USES_MOTIF
+# include <Xm/XmP.h>
+# include <Xm/PrimitiveP.h>
+# include <X11/keysym.h>
+#else
+# include "xintrinsicp.h"
+# include <X11/StringDefs.h>
+#endif
+
+#include "ExternalClientP.h"
+#include "extw-Xt.h"
+
+#ifdef TOOLTALK
+#include TT_C_H_PATH
+#endif
+
+/* This is the client widget, used to communicate with an ExternalShell
+ widget. */
+
+#define NOTIFY(w, type, l0, l1, l2) \
+ extw_send_notify_3(XtDisplay((Widget)(w)), XtWindow((Widget)(w)),\
+ type, l0, l1, l2)
+
+static void externalClientInitialize (Widget req, Widget new, ArgList args,
+ Cardinal *num_args);
+static void externalClientRealize (Widget widget, XtValueMask *mask,
+ XSetWindowAttributes *attrs);
+static void Destroy (Widget w);
+static void EventHandler (Widget wid, XtPointer closure, XEvent *event,
+ Boolean *continue_to_dispatch);
+static void MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
+ Boolean *continue_to_dispatch);
+static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry *,
+ XtWidgetGeometry *);
+static void ExternalClientFocusIn (Widget, XEvent *, String *, Cardinal *);
+static void ExternalClientFocusOut (Widget, XEvent *, String *, Cardinal *);
+static void ExternalClientEnter (Widget, XEvent *, String *, Cardinal *);
+static void ExternalClientLeave (Widget, XEvent *, String *, Cardinal *);
+
+static int my_error_handler(Display *display, XErrorEvent *xev);
+static int (*error_old_handler)(Display *, XErrorEvent *);
+
+static XtResource resources[] = {
+#define offset(field) XtOffset(ExternalClientWidget, externalClient.field)
+ { XtNshellTimeout, XtCShellTimeout, XtRInt, sizeof(int),
+ offset(shell_timeout), XtRImmediate,(XtPointer)DEFAULT_WM_TIMEOUT},
+ { XtNdeadShell, XtCDeadShell, XtRBoolean, sizeof(Boolean),
+ offset(dead_shell), XtRImmediate, (XtPointer)False},
+#ifdef EXTW_USES_MOTIF
+ { XmNnavigationType, XmCNavigationType, XmRNavigationType,
+ sizeof(XmNavigationType), XtOffset(ExternalClientWidget,
+ primitive.navigation_type), XtRImmediate,
+ (XtPointer)XmTAB_GROUP},
+#endif
+ { XtNemacsProcID, XtCEmacsProcID, XtRString, sizeof(String),
+ offset(emacs_procid), XtRImmediate, (XtPointer)NULL},
+ { XtNshellReadyCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList),
+ offset(shell_ready_callback), XtRImmediate, (XtPointer)NULL},
+ { XtNshellName, XtCShellName, XtRString, sizeof(String),
+ offset(shell_name), XtRImmediate, (XtPointer)NULL},
+ { XtNuseToolTalk, XtCUseToolTalk, XtRBoolean, sizeof(Boolean),
+ offset(use_tooltalk), XtRImmediate, (XtPointer)False}
+};
+
+static XtActionsRec actions[] = {
+ {"focusIn", ExternalClientFocusIn},
+ {"focusOut", ExternalClientFocusOut},
+ {"enter", ExternalClientEnter},
+ {"leave", ExternalClientLeave},
+};
+
+ExternalClientClassRec externalClientClassRec = {
+ { /*
+ * core_class fields
+ */
+#ifdef EXTW_USES_MOTIF
+ /* superclass */ (WidgetClass) &xmPrimitiveClassRec,
+#else
+ /* superclass */ (WidgetClass) &coreClassRec,
+#endif
+ /* class_name */ "ExternalClient",
+ /* size */ sizeof(ExternalClientRec),
+ /* Class Initializer */ NULL,
+ /* class_part_initialize*/ NULL, /* XtInheritClassPartInitialize, */
+ /* Class init'ed ? */ FALSE,
+ /* initialize */ externalClientInitialize,
+ /* initialize_notify */ NULL,
+ /* realize */ externalClientRealize,
+ /* actions */ actions,
+ /* num_actions */ XtNumber (actions),
+ /* resources */ resources,
+ /* resource_count */ XtNumber (resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ FALSE,
+ /* compress_exposure */ TRUE,
+ /* compress_enterleave*/ FALSE,
+ /* visible_interest */ TRUE,
+ /* destroy */ Destroy, /* XtInheritDestroy, */
+ /* resize */ XtInheritResize,
+ /* expose */ NULL,
+ /* set_values */ NULL, /* XtInheritSetValues, */
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ NULL,
+ /* accept_focus */ NULL,
+ /* intrinsics version */ XtVersion,
+ /* callback offsets */ NULL,
+ /* tm_table */ "", /* MUST NOT BE NULL or
+ XtInheritTranslations in Motif!!!!!
+ Otherwise keyboard focus translations
+ will not work. */
+ /* query_geometry */ QueryGeometry,
+ /* display_accelerator*/ NULL,
+ /* extension */ NULL
+ },
+#ifdef EXTW_USES_MOTIF
+ {
+ XmInheritBorderHighlight,/* Primitive border_highlight */
+ XmInheritBorderHighlight,/* Primitive border_unhighlight */
+ XtInheritTranslations, /* translations */
+ NULL, /* arm_and_activate */
+ NULL, /* get resources */
+ 0, /* num get_resources */
+ NULL, /* extension */
+ },
+#endif
+ {
+ 0
+ }
+};
+
+WidgetClass externalClientWidgetClass = (WidgetClass) &externalClientClassRec;
+
+static void
+externalClientInitialize (Widget req, Widget new, ArgList args,
+ Cardinal *num_args)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget) new;
+ static int error_handler_added = 0;
+
+ extw_initialize_atoms (XtDisplay (new));
+ extw_which_side = extw_client_send;
+
+#ifdef EXTW_USES_MOTIF
+
+ /* yes I know this is horrible. However, the XmPrimitive class adds
+ the Tab translation in its initialization routine, so we have to
+ override it here. This is all the fault of Xt, which doesn't
+ provide a proper inheritance mechanism for translations.
+
+ -- BPW
+
+ */
+
+ XtOverrideTranslations (new,
+ XtParseTranslationTable ("None<Key>Tab:\n"
+ "<FocusIn>:focusIn()\n"
+ "<FocusOut>:focusOut()\n"
+ "<Enter>:enter()\n"
+ "<Leave>:leave()\n"));
+
+#endif
+
+ XtAddEventHandler (new, 0, TRUE, EventHandler, (XtPointer) NULL);
+
+ ecw->externalClient.shell_ready = False;
+ ecw->externalClient.has_focus = False;
+
+ if (!error_handler_added)
+ {
+ error_handler_added = 1;
+ error_old_handler = XSetErrorHandler (my_error_handler);
+ }
+}
+
+
+#ifdef TOOLTALK
+static Tt_callback_action
+tt_callback(Tt_message m, Tt_pattern p)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget)tt_message_user (m, 0);
+
+ switch (tt_message_state(m))
+ {
+ case TT_FAILED:
+ /* handle errors here */
+ break;
+ case TT_HANDLED:
+ ecw->externalClient.shell_name = tt_message_arg_val (m, 2);
+ XtCallCallbackList ((Widget) ecw,
+ ecw->externalClient.shell_ready_callback, NULL);
+ break;
+ }
+
+ tt_message_destroy (m);
+ return TT_CALLBACK_PROCESSED;
+}
+
+static void
+send_tooltalk_handshake (ExternalClientWidget ecw, Window win, char *name)
+{
+ Tt_message m = tt_message_create ();
+
+ tt_message_op_set (m, "emacs-make-client-screen");
+ tt_message_scope_set (m, TT_SESSION);
+ tt_message_class_set (m, TT_REQUEST);
+ tt_message_arg_add (m, TT_IN, "string", name);
+ tt_message_iarg_add (m, TT_IN, "int", win);
+ tt_message_arg_add (m, TT_OUT, "string", NULL);
+ tt_message_user_set (m, 0, (void *)ecw);
+ tt_message_callback_add (m, tt_callback);
+ if (ecw->externalClient.emacs_procid)
+ {
+ tt_message_address_set (m, TT_HANDLER);
+ tt_message_handler_set (m, ecw->externalClient.emacs_procid);
+ }
+ else
+ tt_message_address_set (m, TT_PROCEDURE);
+ tt_message_send (m);
+}
+
+#endif
+
+
+static void
+externalClientRealize (Widget w, XtValueMask *vm, XSetWindowAttributes *attrs)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget)w;
+
+#ifdef EXTW_USES_MOTIF
+ (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
+#else
+ (*coreWidgetClass->core_class.realize) (w, vm, attrs);
+#endif
+
+#ifdef TOOLTALK
+
+ /* Make sure that the server actually knows about this window id before
+ * telling Emacs about it.
+ */
+ if (ecw->externalClient.use_tooltalk)
+ {
+ XSync (XtDisplay (w), False);
+ send_tooltalk_handshake (ecw, XtWindow (w), XtName (w));
+ }
+#endif
+}
+
+
+/***********************************************************************/
+
+/* window-to-widget list. */
+
+struct ww_list
+{
+ Window win;
+ Widget wid;
+ struct ww_list *next;
+};
+
+struct ww_list ww_list[1];
+
+static int
+add_ww (Window win, Widget wid)
+{
+ struct ww_list *ww = (struct ww_list *) malloc (sizeof (struct
+ ww_list));
+ if (!ww)
+ return 0;
+ ww->win = win;
+ ww->wid = wid;
+ ww->next = ww_list->next;
+ ww_list->next = ww;
+ return 1;
+}
+
+static Widget
+remove_ww (Window win)
+{
+ struct ww_list *w1, *w2;
+ Widget wid = 0;
+
+ for (w1=ww_list, w2=w1->next; w2; w1=w2, w2=w2->next)
+ if (w2->win == win)
+ {
+ w1->next = w2->next;
+ wid = w2->wid;
+ free (w2);
+ break;
+ }
+ return wid;
+}
+
+/***********************************************************************/
+
+/* stolen outright from Intrinsic.c */
+
+static void ComputeWindowAttributes(widget,value_mask,values)
+ Widget widget;
+ XtValueMask *value_mask;
+ XSetWindowAttributes *values;
+{
+ *value_mask = CWEventMask | CWColormap;
+ (*values).event_mask = XtBuildEventMask(widget);
+ (*values).colormap = widget->core.colormap;
+ if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
+ *value_mask |= CWBackPixmap;
+ (*values).background_pixmap = widget->core.background_pixmap;
+ } else {
+ *value_mask |= CWBackPixel;
+ (*values).background_pixel = widget->core.background_pixel;
+ }
+ if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
+ *value_mask |= CWBorderPixmap;
+ (*values).border_pixmap = widget->core.border_pixmap;
+ } else {
+ *value_mask |= CWBorderPixel;
+ (*values).border_pixel = widget->core.border_pixel;
+ }
+ if (widget->core.widget_class->core_class.expose == (XtExposeProc) NULL) {
+ /* Try to avoid redisplay upon resize by making bit_gravity the same
+ as the default win_gravity */
+ *value_mask |= CWBitGravity;
+ (*values).bit_gravity = NorthWestGravity;
+ }
+} /* ComputeWindowAttributes */
+
+static void
+end_connection (ExternalClientWidget w)
+{
+ XSetWindowAttributes xswa;
+ XtValueMask mask;
+ Widget wid = (Widget) w;
+
+ w->externalClient.shell_ready = False;
+ XtRemoveEventHandler (wid, w->externalClient.event_mask,
+ FALSE, MaskableEventHandler, (XtPointer) NULL);
+ ComputeWindowAttributes (wid, &mask, &xswa);
+ XChangeWindowAttributes (XtDisplay (wid), XtWindow (wid), mask, &xswa);
+ XClearArea (XtDisplay (wid), XtWindow (wid), 0, 0, 0, 0, True);
+}
+
+static int
+my_error_handler (Display *display, XErrorEvent *xev)
+{
+ Widget wid;
+
+ if (xev->error_code != BadWindow)
+ goto call_old;
+ wid = remove_ww (xev->resourceid);
+ if (wid)
+ {
+ end_connection ((ExternalClientWidget) wid);
+ return 0;
+ }
+
+ call_old:
+ return error_old_handler (display, xev);
+}
+
+static void
+MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
+ Boolean *continue_to_dispatch)
+ /* closure and continue_to_dispatch unused */
+{
+ ExternalClientWidget w = (ExternalClientWidget) wid;
+
+ if (w->externalClient.shell_ready)
+ {
+ if (event->type == KeyPress || event->type == KeyRelease ||
+ event->type == ButtonPress || event->type == ButtonRelease ||
+ event->type == MotionNotify)
+ event->xkey.subwindow = 0;
+#ifdef EXTW_USES_MOTIF
+ /* hackkkkkkkkkkkkkk! Suppress CTRL-TAB, SHIFT-TAB, etc. so that
+ Emacs doesn't attempt to interpret focus-change keystrokes. */
+ if (event->type == KeyPress &&
+ XLookupKeysym ((XKeyEvent *) event, 0) == XK_Tab &&
+ (event->xkey.state & ControlMask ||
+ event->xkey.state & ShiftMask))
+ return;
+#endif
+ event->xany.window = w->core.window;
+ XSendEvent (XtDisplay (wid), w->externalClient.event_window, FALSE, 0,
+ event);
+ XSync (XtDisplay (wid), 0); /* make sure that any BadWindow errors
+ (meaning the server died) get handled
+ before XSendEvent is called again. */
+
+ }
+}
+
+static void
+EventHandler (Widget wid, XtPointer closure, XEvent *event,
+ Boolean *continue_to_dispatch)
+ /* closure and continue_to_dispatch unused */
+{
+ ExternalClientWidget w = (ExternalClientWidget) wid;
+
+ if (w->core.window != event->xany.window)
+ {
+ XtAppErrorMsg (XtWidgetToApplicationContext (wid),
+ "invalidWindow","eventHandler",XtCXtToolkitError,
+ "Event with wrong window",
+ (String *)NULL, (Cardinal *)NULL);
+ return;
+ }
+
+ if (event->type == ClientMessage &&
+ event->xclient.message_type == a_EXTW_NOTIFY &&
+ event->xclient.data.l[0] == extw_shell_send)
+ switch (event->xclient.data.l[1])
+ {
+
+ case extw_notify_qg:
+ /* shell is alive again. */
+
+ w->externalClient.dead_shell = False;
+ break;
+
+ case extw_notify_gm:
+ {
+ XtWidgetGeometry xwg, xwg_return;
+ XtGeometryResult result;
+
+ extw_get_geometry_value (XtDisplay (wid), XtWindow (wid),
+ a_EXTW_GEOMETRY_MANAGER, &xwg);
+ result = XtMakeGeometryRequest (wid, &xwg, &xwg_return);
+
+ extw_send_geometry_value (XtDisplay (wid), XtWindow (wid),
+ a_EXTW_GEOMETRY_MANAGER, extw_notify_gm,
+ result == XtGeometryAlmost ? &xwg_return :
+ NULL, result);
+ break;
+ }
+
+ case extw_notify_init:
+ w->externalClient.shell_ready = True;
+ w->externalClient.event_window = event->xclient.data.l[2];
+ w->externalClient.event_mask = event->xclient.data.l[3];
+ add_ww (w->externalClient.event_window, (Widget) w);
+
+ XtAddEventHandler (wid, w->externalClient.event_mask,
+ FALSE, MaskableEventHandler, (XtPointer) NULL);
+#ifdef EXTW_USES_MOTIF
+ NOTIFY (w, extw_notify_init,
+ EXTW_TYPE_MOTIF,
+ 0, 0);
+#else
+ NOTIFY (w, extw_notify_init,
+ EXTW_TYPE_XT,
+ 0, 0);
+#endif
+ break;
+
+ case extw_notify_end:
+ end_connection (w);
+ remove_ww (w->externalClient.event_window);
+ break;
+
+ case extw_notify_set_focus:
+#ifdef EXTW_USES_MOTIF
+ XmProcessTraversal (wid, XmTRAVERSE_CURRENT);
+#else
+ XtSetKeyboardFocus (wid, None);
+#endif
+ break;
+
+ }
+}
+
+static void Destroy(wid)
+ Widget wid;
+{
+ ExternalClientWidget w = (ExternalClientWidget)wid;
+
+ NOTIFY(w, extw_notify_end, 0, 0, 0);
+}
+
+static XtGeometryResult QueryGeometry(gw, request, reply)
+ Widget gw;
+ XtWidgetGeometry *request, *reply;
+{
+ ExternalClientWidget w = (ExternalClientWidget)gw;
+ XEvent event;
+ unsigned long request_num;
+ Display *display = XtDisplay(gw);
+ XtWidgetGeometry req = *request; /* don't modify caller's structure */
+
+ if (!XtIsRealized((Widget)w) || !w->externalClient.shell_ready)
+ return XtGeometryYes;
+
+ if (w->externalClient.dead_shell == TRUE)
+ /* The shell is sick. */
+ return XtGeometryNo;
+
+ req.sibling = None;
+ req.request_mode &= ~CWSibling;
+ request_num = NextRequest(display);
+ extw_send_geometry_value(XtDisplay(gw), XtWindow(gw), a_EXTW_QUERY_GEOMETRY,
+ extw_notify_qg, &req, 0);
+
+ if (extw_wait_for_response(gw, &event, request_num, extw_notify_qg,
+ w->externalClient.shell_timeout)) {
+ XtGeometryResult result = (XtGeometryResult) event.xclient.data.l[0];
+
+ if (result == XtGeometryAlmost) {
+ extw_get_geometry_value(XtDisplay(gw), XtWindow(gw),
+ a_EXTW_QUERY_GEOMETRY, reply);
+ }
+ return result;
+ } else {
+ w->externalClient.dead_shell = TRUE; /* timed out; must be broken */
+ return XtGeometryNo;
+ }
+}
+
+static void ExternalClientFocusIn (Widget w, XEvent *event, String *params,
+ Cardinal *num_params)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget) w;
+
+ if (event->xfocus.send_event && !ecw->externalClient.has_focus) {
+ ecw->externalClient.has_focus = True;
+ NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
+ }
+#ifdef EXTW_USES_MOTIF
+ _XmPrimitiveFocusIn (w, event, params, num_params);
+#endif
+}
+
+static void ExternalClientFocusOut (Widget w, XEvent *event, String *params,
+ Cardinal *num_params)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget) w;
+
+ if (event->xfocus.send_event && ecw->externalClient.has_focus) {
+ ecw->externalClient.has_focus = False;
+ NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
+ }
+#ifdef EXTW_USES_MOTIF
+ _XmPrimitiveFocusOut(w, event, params, num_params);
+#endif
+}
+
+static void ExternalClientEnter (Widget w, XEvent *event, String *params,
+ Cardinal *num_params)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget) w;
+
+ if (
+#ifdef EXTW_USES_MOTIF
+ _XmGetFocusPolicy (w) != XmEXPLICIT &&
+#endif
+ !ecw->externalClient.has_focus &&
+ event->xcrossing.focus && event->xcrossing.detail != NotifyInferior) {
+ ecw->externalClient.has_focus = True;
+ NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
+ }
+#ifdef EXTW_USES_MOTIF
+ _XmPrimitiveEnter (w, event, params, num_params);
+#endif
+}
+
+static void ExternalClientLeave (Widget w, XEvent *event, String *params,
+ Cardinal *num_params)
+{
+ ExternalClientWidget ecw = (ExternalClientWidget) w;
+
+ if (
+#ifdef EXTW_USES_MOTIF
+ _XmGetFocusPolicy (w) != XmEXPLICIT &&
+#endif
+ ecw->externalClient.has_focus &&
+ event->xcrossing.focus && event->xcrossing.detail != NotifyInferior) {
+ ecw->externalClient.has_focus = False;
+ NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
+ }
+#ifdef EXTW_USES_MOTIF
+ _XmPrimitiveLeave (w, event, params, num_params);
+#endif
+}