1 /* External client widget.
2 Copyright (C) 1993, 1994 Sun Microsystems, Inc.
4 This file is part of SXEmacs
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Synched up with: Not in FSF. */
22 /* Written by Ben Wing, September 1993. */
28 #ifndef EXTERNAL_WIDGET
29 ERROR ! This ought not be getting compiled if EXTERNAL_WIDGET
36 #ifdef EXTW_USES_MOTIF
38 # include <Xm/PrimitiveP.h>
39 # include <X11/keysym.h>
41 # include "xintrinsicp.h"
42 # include <X11/StringDefs.h>
44 #include "ExternalClientP.h"
45 #include "ui/X11/extw-Xt.h"
49 /* This is the client widget, used to communicate with an ExternalShell
51 #define NOTIFY(w, type, l0, l1, l2) \
52 extw_send_notify_3(XtDisplay((Widget)(w)), XtWindow((Widget)(w)),\
54 static void externalClientInitialize(Widget req, Widget new,
55 ArgList args, Cardinal * num_args);
56 static void externalClientRealize(Widget widget, XtValueMask * mask,
57 XSetWindowAttributes * attrs);
58 static void Destroy(Widget w);
59 static void EventHandler(Widget wid, XtPointer closure, XEvent * event,
60 Boolean * continue_to_dispatch);
61 static void MaskableEventHandler(Widget wid, XtPointer closure, XEvent * event,
62 Boolean * continue_to_dispatch);
63 static XtGeometryResult QueryGeometry(Widget, XtWidgetGeometry *,
65 static void ExternalClientFocusIn(Widget, XEvent *, String *, Cardinal *);
66 static void ExternalClientFocusOut(Widget, XEvent *, String *, Cardinal *);
67 static void ExternalClientEnter(Widget, XEvent *, String *, Cardinal *);
68 static void ExternalClientLeave(Widget, XEvent *, String *, Cardinal *);
70 static int my_error_handler(Display * display, XErrorEvent * xev);
71 static int (*error_old_handler) (Display *, XErrorEvent *);
73 static XtResource resources[] = {
74 #define offset(field) XtOffset(ExternalClientWidget, externalClient.field)
75 {XtNshellTimeout, XtCShellTimeout,
77 offset(shell_timeout), XtRImmediate, (XtPointer) DEFAULT_WM_TIMEOUT},
78 {XtNdeadShell, XtCDeadShell,
79 XtRBoolean, sizeof(Boolean),
80 offset(dead_shell), XtRImmediate, (XtPointer) False}
82 #ifdef EXTW_USES_MOTIF
83 {XmNnavigationType, XmCNavigationType,
84 XmRNavigationType, sizeof(XmNavigationType),
85 XtOffset(ExternalClientWidget, primitive.navigation_type),
86 XtRImmediate, (XtPointer) XmTAB_GROUP}
89 {XtNemacsProcID, XtCEmacsProcID,
90 XtRString, sizeof(String),
91 offset(emacs_procid), XtRImmediate, (XtPointer) NULL}
93 {XtNshellReadyCallback, XtCCallback,
94 XtRCallback, sizeof(XtCallbackList),
95 offset(shell_ready_callback), XtRImmediate, (XtPointer) NULL}
97 {XtNshellName, XtCShellName,
98 XtRString, sizeof(String),
99 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,
126 /* XtInheritClassPartInitialize, */
127 /* Class init'ed ? */ FALSE,
128 /* initialize */ externalClientInitialize,
129 /* initialize_notify */ NULL,
130 /* realize */ externalClientRealize,
131 /* actions */ actions,
132 /* num_actions */ XtNumber(actions),
133 /* resources */ resources,
134 /* resource_count */ XtNumber(resources),
135 /* xrm_class */ NULLQUARK,
136 /* compress_motion */ FALSE,
137 /* compress_exposure */ TRUE,
138 /* compress_enterleave */ FALSE,
139 /* visible_interest */ TRUE,
140 /* destroy */ Destroy,
141 /* XtInheritDestroy, */
142 /* resize */ XtInheritResize,
144 /* set_values */ NULL,
145 /* XtInheritSetValues, */
146 /* set_values_hook */ NULL,
147 /* set_values_almost */ XtInheritSetValuesAlmost,
148 /* get_values_hook */ NULL,
149 /* accept_focus */ NULL,
150 /* intrinsics version */ XtVersion,
151 /* callback offsets */ NULL,
153 /* MUST NOT BE NULL or
154 XtInheritTranslations in Motif!!!!!
155 Otherwise keyboard focus translations
157 /* query_geometry */ QueryGeometry,
158 /* display_accelerator */ NULL,
162 #ifdef EXTW_USES_MOTIF
164 XmInheritBorderHighlight, /* Primitive border_highlight */
165 XmInheritBorderHighlight, /* Primitive border_unhighlight */
166 XtInheritTranslations, /* translations */
167 NULL, /* arm_and_activate */
168 NULL, /* get resources */
169 0, /* num get_resources */
170 NULL, /* extension */
178 WidgetClass externalClientWidgetClass = (WidgetClass) & externalClientClassRec;
181 externalClientInitialize(Widget req, Widget new, ArgList args,
184 ExternalClientWidget ecw = (ExternalClientWidget) new;
185 static int error_handler_added = 0;
187 extw_initialize_atoms(XtDisplay(new));
188 extw_which_side = extw_client_send;
190 #ifdef EXTW_USES_MOTIF
192 /* yes I know this is horrible. However, the XmPrimitive class adds
193 the Tab translation in its initialization routine, so we have to
194 override it here. This is all the fault of Xt, which doesn't
195 provide a proper inheritance mechanism for translations.
201 XtOverrideTranslations(new,
202 XtParseTranslationTable("None<Key>Tab:\n"
203 "<FocusIn>:focusIn()\n"
204 "<FocusOut>:focusOut()\n"
206 "<Leave>:leave()\n"));
210 XtAddEventHandler(new, 0, TRUE, EventHandler, (XtPointer) NULL);
212 ecw->externalClient.shell_ready = False;
213 ecw->externalClient.has_focus = False;
215 if (!error_handler_added) {
216 error_handler_added = 1;
217 error_old_handler = XSetErrorHandler(my_error_handler);
222 static Tt_callback_action tt_callback(Tt_message m, Tt_pattern p)
224 ExternalClientWidget ecw = (ExternalClientWidget) tt_message_user(m, 0);
226 switch (tt_message_state(m)) {
228 /* handle errors here */
231 ecw->externalClient.shell_name = tt_message_arg_val(m, 2);
232 XtCallCallbackList((Widget) ecw,
233 ecw->externalClient.shell_ready_callback,
238 tt_message_destroy(m);
239 return TT_CALLBACK_PROCESSED;
243 send_tooltalk_handshake(ExternalClientWidget ecw, Window win, char *name)
245 Tt_message m = tt_message_create();
247 tt_message_op_set(m, "emacs-make-client-screen");
248 tt_message_scope_set(m, TT_SESSION);
249 tt_message_class_set(m, TT_REQUEST);
250 tt_message_arg_add(m, TT_IN, "string", name);
251 tt_message_iarg_add(m, TT_IN, "int", win);
252 tt_message_arg_add(m, TT_OUT, "string", NULL);
253 tt_message_user_set(m, 0, (void *)ecw);
254 tt_message_callback_add(m, tt_callback);
255 if (ecw->externalClient.emacs_procid) {
256 tt_message_address_set(m, TT_HANDLER);
257 tt_message_handler_set(m, ecw->externalClient.emacs_procid);
259 tt_message_address_set(m, TT_PROCEDURE);
266 externalClientRealize(Widget w, XtValueMask * vm, XSetWindowAttributes * attrs)
268 ExternalClientWidget ecw = (ExternalClientWidget) w;
270 #ifdef EXTW_USES_MOTIF
271 (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
273 (*coreWidgetClass->core_class.realize) (w, vm, attrs);
278 /* Make sure that the server actually knows about this window id before
279 * telling Emacs about it.
281 if (ecw->externalClient.use_tooltalk) {
282 XSync(XtDisplay(w), False);
283 send_tooltalk_handshake(ecw, XtWindow(w), XtName(w));
288 /***********************************************************************/
290 /* window-to-widget list. */
295 struct ww_list *next;
298 struct ww_list ww_list[1];
300 static int add_ww(Window win, Widget wid)
302 struct ww_list *ww = (struct ww_list *)malloc(sizeof(struct ww_list));
307 ww->next = ww_list->next;
312 static Widget remove_ww(Window win)
314 struct ww_list *w1, *w2;
317 for (w1 = ww_list, w2 = w1->next; w2; w1 = w2, w2 = w2->next)
318 if (w2->win == win) {
327 /***********************************************************************/
329 /* stolen outright from Intrinsic.c */
331 static void ComputeWindowAttributes(widget, value_mask, values)
333 XtValueMask *value_mask;
334 XSetWindowAttributes *values;
336 *value_mask = CWEventMask | CWColormap;
337 (*values).event_mask = XtBuildEventMask(widget);
338 (*values).colormap = widget->core.colormap;
339 if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
340 *value_mask |= CWBackPixmap;
341 (*values).background_pixmap = widget->core.background_pixmap;
343 *value_mask |= CWBackPixel;
344 (*values).background_pixel = widget->core.background_pixel;
346 if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
347 *value_mask |= CWBorderPixmap;
348 (*values).border_pixmap = widget->core.border_pixmap;
350 *value_mask |= CWBorderPixel;
351 (*values).border_pixel = widget->core.border_pixel;
353 if (widget->core.widget_class->core_class.expose == (XtExposeProc) NULL) {
354 /* Try to avoid redisplay upon resize by making bit_gravity the same
355 as the default win_gravity */
356 *value_mask |= CWBitGravity;
357 (*values).bit_gravity = NorthWestGravity;
359 } /* ComputeWindowAttributes */
361 static void end_connection(ExternalClientWidget w)
363 XSetWindowAttributes xswa;
365 Widget wid = (Widget) w;
367 w->externalClient.shell_ready = False;
368 XtRemoveEventHandler(wid, w->externalClient.event_mask,
369 FALSE, MaskableEventHandler, (XtPointer) NULL);
370 ComputeWindowAttributes(wid, &mask, &xswa);
371 XChangeWindowAttributes(XtDisplay(wid), XtWindow(wid), mask, &xswa);
372 XClearArea(XtDisplay(wid), XtWindow(wid), 0, 0, 0, 0, True);
375 static int my_error_handler(Display * display, XErrorEvent * xev)
379 if (xev->error_code != BadWindow)
381 wid = remove_ww(xev->resourceid);
383 end_connection((ExternalClientWidget) wid);
388 return error_old_handler(display, xev);
392 MaskableEventHandler(Widget wid, XtPointer closure, XEvent * event,
393 Boolean * continue_to_dispatch)
394 /* closure and continue_to_dispatch unused */
396 ExternalClientWidget w = (ExternalClientWidget) wid;
398 if (w->externalClient.shell_ready) {
399 if (event->type == KeyPress || event->type == KeyRelease ||
400 event->type == ButtonPress || event->type == ButtonRelease
401 || event->type == MotionNotify)
402 event->xkey.subwindow = 0;
403 #ifdef EXTW_USES_MOTIF
404 /* hackkkkkkkkkkkkkk! Suppress CTRL-TAB, SHIFT-TAB, etc. so that
405 Emacs doesn't attempt to interpret focus-change keystrokes. */
406 if (event->type == KeyPress &&
407 XLookupKeysym((XKeyEvent *) event, 0) == XK_Tab &&
408 (event->xkey.state & ControlMask ||
409 event->xkey.state & ShiftMask))
412 event->xany.window = w->core.window;
413 XSendEvent(XtDisplay(wid), w->externalClient.event_window,
415 XSync(XtDisplay(wid), 0); /* make sure that any BadWindow errors
416 (meaning the server died) get handled
417 before XSendEvent is called again. */
423 EventHandler(Widget wid, XtPointer closure, XEvent * event,
424 Boolean * continue_to_dispatch)
425 /* closure and continue_to_dispatch unused */
427 ExternalClientWidget w = (ExternalClientWidget) wid;
429 if (w->core.window != event->xany.window) {
430 XtAppErrorMsg(XtWidgetToApplicationContext(wid),
431 "invalidWindow", "eventHandler",
432 XtCXtToolkitError, "Event with wrong window",
433 (String *) NULL, (Cardinal *) NULL);
437 if (event->type == ClientMessage &&
438 event->xclient.message_type == a_EXTW_NOTIFY &&
439 event->xclient.data.l[0] == extw_shell_send)
440 switch (event->xclient.data.l[1]) {
443 /* shell is alive again. */
445 w->externalClient.dead_shell = False;
450 XtWidgetGeometry xwg, xwg_return;
451 XtGeometryResult result;
453 extw_get_geometry_value(XtDisplay(wid),
455 a_EXTW_GEOMETRY_MANAGER,
458 XtMakeGeometryRequest(wid, &xwg,
461 extw_send_geometry_value(XtDisplay(wid),
463 a_EXTW_GEOMETRY_MANAGER,
472 case extw_notify_init:
473 w->externalClient.shell_ready = True;
474 w->externalClient.event_window =
475 event->xclient.data.l[2];
476 w->externalClient.event_mask = event->xclient.data.l[3];
477 add_ww(w->externalClient.event_window, (Widget) w);
479 XtAddEventHandler(wid, w->externalClient.event_mask,
480 FALSE, MaskableEventHandler,
482 #ifdef EXTW_USES_MOTIF
483 NOTIFY(w, extw_notify_init, EXTW_TYPE_MOTIF, 0, 0);
485 NOTIFY(w, extw_notify_init, EXTW_TYPE_XT, 0, 0);
489 case extw_notify_end:
491 remove_ww(w->externalClient.event_window);
494 case extw_notify_set_focus:
495 #ifdef EXTW_USES_MOTIF
496 XmProcessTraversal(wid, XmTRAVERSE_CURRENT);
498 XtSetKeyboardFocus(wid, None);
505 static void Destroy(wid)
508 ExternalClientWidget w = (ExternalClientWidget) wid;
510 NOTIFY(w, extw_notify_end, 0, 0, 0);
513 static XtGeometryResult QueryGeometry(gw, request, reply)
515 XtWidgetGeometry *request, *reply;
517 ExternalClientWidget w = (ExternalClientWidget) gw;
519 unsigned long request_num;
520 Display *display = XtDisplay(gw);
521 XtWidgetGeometry req = *request; /* don't modify caller's structure */
523 if (!XtIsRealized((Widget) w) || !w->externalClient.shell_ready)
524 return XtGeometryYes;
526 if (w->externalClient.dead_shell == TRUE)
527 /* The shell is sick. */
531 req.request_mode &= ~CWSibling;
532 request_num = NextRequest(display);
533 extw_send_geometry_value(XtDisplay(gw), XtWindow(gw),
534 a_EXTW_QUERY_GEOMETRY, extw_notify_qg, &req,
537 if (extw_wait_for_response(gw, &event, request_num, extw_notify_qg,
538 w->externalClient.shell_timeout)) {
539 XtGeometryResult result =
540 (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
592 && event->xcrossing.detail != NotifyInferior) {
593 ecw->externalClient.has_focus = True;
594 NOTIFY(ecw, extw_notify_focus_in, 0, 0, 0);
596 #ifdef EXTW_USES_MOTIF
597 _XmPrimitiveEnter(w, event, params, num_params);
601 static void ExternalClientLeave(Widget w, XEvent * event, String * params,
602 Cardinal * num_params)
604 ExternalClientWidget ecw = (ExternalClientWidget) w;
607 #ifdef EXTW_USES_MOTIF
608 _XmGetFocusPolicy(w) != XmEXPLICIT &&
610 ecw->externalClient.has_focus &&
611 event->xcrossing.focus
612 && event->xcrossing.detail != NotifyInferior) {
613 ecw->externalClient.has_focus = False;
614 NOTIFY(ecw, extw_notify_focus_out, 0, 0, 0);
616 #ifdef EXTW_USES_MOTIF
617 _XmPrimitiveLeave(w, event, params, num_params);