diff src/ExternalClient.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 538048ae2ab8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ExternalClient.c	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,617 @@
+/* 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>
+#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
+}