comparison src/ExternalClient.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 8de8e3f6228a
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
1 /* External client widget.
2 Copyright (C) 1993, 1994 Sun Microsystems, Inc.
3
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.
8
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.
13
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. */
18
19 /* Synched up with: Not in FSF. */
20
21 /* Written by Ben Wing, September 1993. */
22
23 #ifdef emacs
24
25 #include <config.h>
26
27 #ifndef EXTERNAL_WIDGET
28 ERROR! This ought not be getting compiled if EXTERNAL_WIDGET is undefined
29 #endif
30
31 #endif /* emacs */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #ifdef EXTW_USES_MOTIF
37 # include <Xm/XmP.h>
38 # include <Xm/PrimitiveP.h>
39 # include <X11/keysym.h>
40 #else
41 # include "xintrinsicp.h"
42 # include <X11/StringDefs.h>
43 #endif
44
45 #include "ExternalClientP.h"
46 #include "extw-Xt.h"
47
48 #ifdef TOOLTALK
49 #include TT_C_H_PATH
50 #endif
51
52 /* This is the client widget, used to communicate with an ExternalShell
53 widget. */
54
55 #define NOTIFY(w, type, l0, l1, l2) \
56 extw_send_notify_3(XtDisplay((Widget)(w)), XtWindow((Widget)(w)),\
57 type, l0, l1, l2)
58
59 static void externalClientInitialize (Widget req, Widget new, ArgList args,
60 Cardinal *num_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 *,
69 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 *);
74
75 static int my_error_handler(Display *display, XErrorEvent *xev);
76 static int (*error_old_handler)(Display *, XErrorEvent *);
77
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},
89 #endif
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}
98 };
99
100 static XtActionsRec actions[] = {
101 {"focusIn", ExternalClientFocusIn},
102 {"focusOut", ExternalClientFocusOut},
103 {"enter", ExternalClientEnter},
104 {"leave", ExternalClientLeave},
105 };
106
107 ExternalClientClassRec externalClientClassRec = {
108 { /*
109 * core_class fields
110 */
111 #ifdef EXTW_USES_MOTIF
112 /* superclass */ (WidgetClass) &xmPrimitiveClassRec,
113 #else
114 /* superclass */ (WidgetClass) &coreClassRec,
115 #endif
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,
135 /* expose */ NULL,
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
146 will not work. */
147 /* query_geometry */ QueryGeometry,
148 /* display_accelerator*/ NULL,
149 /* extension */ NULL
150 },
151 #ifdef EXTW_USES_MOTIF
152 {
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 */
160 },
161 #endif
162 {
163 0
164 }
165 };
166
167 WidgetClass externalClientWidgetClass = (WidgetClass) &externalClientClassRec;
168
169 static void
170 externalClientInitialize (Widget req, Widget new, ArgList args,
171 Cardinal *num_args)
172 {
173 ExternalClientWidget ecw = (ExternalClientWidget) new;
174 static int error_handler_added = 0;
175
176 extw_initialize_atoms (XtDisplay (new));
177 extw_which_side = extw_client_send;
178
179 #ifdef EXTW_USES_MOTIF
180
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.
185
186 -- BPW
187
188 */
189
190 XtOverrideTranslations (new,
191 XtParseTranslationTable ("None<Key>Tab:\n"
192 "<FocusIn>:focusIn()\n"
193 "<FocusOut>:focusOut()\n"
194 "<Enter>:enter()\n"
195 "<Leave>:leave()\n"));
196
197 #endif
198
199 XtAddEventHandler (new, 0, TRUE, EventHandler, (XtPointer) NULL);
200
201 ecw->externalClient.shell_ready = False;
202 ecw->externalClient.has_focus = False;
203
204 if (!error_handler_added)
205 {
206 error_handler_added = 1;
207 error_old_handler = XSetErrorHandler (my_error_handler);
208 }
209 }
210
211
212 #ifdef TOOLTALK
213 static Tt_callback_action
214 tt_callback(Tt_message m, Tt_pattern p)
215 {
216 ExternalClientWidget ecw = (ExternalClientWidget)tt_message_user (m, 0);
217
218 switch (tt_message_state(m))
219 {
220 case TT_FAILED:
221 /* handle errors here */
222 break;
223 case TT_HANDLED:
224 ecw->externalClient.shell_name = tt_message_arg_val (m, 2);
225 XtCallCallbackList ((Widget) ecw,
226 ecw->externalClient.shell_ready_callback, NULL);
227 break;
228 }
229
230 tt_message_destroy (m);
231 return TT_CALLBACK_PROCESSED;
232 }
233
234 static void
235 send_tooltalk_handshake (ExternalClientWidget ecw, Window win, char *name)
236 {
237 Tt_message m = tt_message_create ();
238
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)
248 {
249 tt_message_address_set (m, TT_HANDLER);
250 tt_message_handler_set (m, ecw->externalClient.emacs_procid);
251 }
252 else
253 tt_message_address_set (m, TT_PROCEDURE);
254 tt_message_send (m);
255 }
256
257 #endif
258
259
260 static void
261 externalClientRealize (Widget w, XtValueMask *vm, XSetWindowAttributes *attrs)
262 {
263 ExternalClientWidget ecw = (ExternalClientWidget)w;
264
265 #ifdef EXTW_USES_MOTIF
266 (*xmPrimitiveWidgetClass->core_class.realize) (w, vm, attrs);
267 #else
268 (*coreWidgetClass->core_class.realize) (w, vm, attrs);
269 #endif
270
271 #ifdef TOOLTALK
272
273 /* Make sure that the server actually knows about this window id before
274 * telling Emacs about it.
275 */
276 if (ecw->externalClient.use_tooltalk)
277 {
278 XSync (XtDisplay (w), False);
279 send_tooltalk_handshake (ecw, XtWindow (w), XtName (w));
280 }
281 #endif
282 }
283
284
285 /***********************************************************************/
286
287 /* window-to-widget list. */
288
289 struct ww_list
290 {
291 Window win;
292 Widget wid;
293 struct ww_list *next;
294 };
295
296 struct ww_list ww_list[1];
297
298 static int
299 add_ww (Window win, Widget wid)
300 {
301 struct ww_list *ww = (struct ww_list *) malloc (sizeof (struct
302 ww_list));
303 if (!ww)
304 return 0;
305 ww->win = win;
306 ww->wid = wid;
307 ww->next = ww_list->next;
308 ww_list->next = ww;
309 return 1;
310 }
311
312 static Widget
313 remove_ww (Window win)
314 {
315 struct ww_list *w1, *w2;
316 Widget wid = 0;
317
318 for (w1=ww_list, w2=w1->next; w2; w1=w2, w2=w2->next)
319 if (w2->win == win)
320 {
321 w1->next = w2->next;
322 wid = w2->wid;
323 free (w2);
324 break;
325 }
326 return wid;
327 }
328
329 /***********************************************************************/
330
331 /* stolen outright from Intrinsic.c */
332
333 static void ComputeWindowAttributes(widget,value_mask,values)
334 Widget widget;
335 XtValueMask *value_mask;
336 XSetWindowAttributes *values;
337 {
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;
344 } else {
345 *value_mask |= CWBackPixel;
346 (*values).background_pixel = widget->core.background_pixel;
347 }
348 if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
349 *value_mask |= CWBorderPixmap;
350 (*values).border_pixmap = widget->core.border_pixmap;
351 } else {
352 *value_mask |= CWBorderPixel;
353 (*values).border_pixel = widget->core.border_pixel;
354 }
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;
360 }
361 } /* ComputeWindowAttributes */
362
363 static void
364 end_connection (ExternalClientWidget w)
365 {
366 XSetWindowAttributes xswa;
367 XtValueMask mask;
368 Widget wid = (Widget) w;
369
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);
376 }
377
378 static int
379 my_error_handler (Display *display, XErrorEvent *xev)
380 {
381 Widget wid;
382
383 if (xev->error_code != BadWindow)
384 goto call_old;
385 wid = remove_ww (xev->resourceid);
386 if (wid)
387 {
388 end_connection ((ExternalClientWidget) wid);
389 return 0;
390 }
391
392 call_old:
393 return error_old_handler (display, xev);
394 }
395
396 static void
397 MaskableEventHandler (Widget wid, XtPointer closure, XEvent *event,
398 Boolean *continue_to_dispatch)
399 /* closure and continue_to_dispatch unused */
400 {
401 ExternalClientWidget w = (ExternalClientWidget) wid;
402
403 if (w->externalClient.shell_ready)
404 {
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))
416 return;
417 #endif
418 event->xany.window = w->core.window;
419 XSendEvent (XtDisplay (wid), w->externalClient.event_window, FALSE, 0,
420 event);
421 XSync (XtDisplay (wid), 0); /* make sure that any BadWindow errors
422 (meaning the server died) get handled
423 before XSendEvent is called again. */
424
425 }
426 }
427
428 static void
429 EventHandler (Widget wid, XtPointer closure, XEvent *event,
430 Boolean *continue_to_dispatch)
431 /* closure and continue_to_dispatch unused */
432 {
433 ExternalClientWidget w = (ExternalClientWidget) wid;
434
435 if (w->core.window != event->xany.window)
436 {
437 XtAppErrorMsg (XtWidgetToApplicationContext (wid),
438 "invalidWindow","eventHandler",XtCXtToolkitError,
439 "Event with wrong window",
440 (String *)NULL, (Cardinal *)NULL);
441 return;
442 }
443
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])
448 {
449
450 case extw_notify_qg:
451 /* shell is alive again. */
452
453 w->externalClient.dead_shell = False;
454 break;
455
456 case extw_notify_gm:
457 {
458 XtWidgetGeometry xwg, xwg_return;
459 XtGeometryResult result;
460
461 extw_get_geometry_value (XtDisplay (wid), XtWindow (wid),
462 a_EXTW_GEOMETRY_MANAGER, &xwg);
463 result = XtMakeGeometryRequest (wid, &xwg, &xwg_return);
464
465 extw_send_geometry_value (XtDisplay (wid), XtWindow (wid),
466 a_EXTW_GEOMETRY_MANAGER, extw_notify_gm,
467 result == XtGeometryAlmost ? &xwg_return :
468 NULL, result);
469 break;
470 }
471
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);
477
478 XtAddEventHandler (wid, w->externalClient.event_mask,
479 FALSE, MaskableEventHandler, (XtPointer) NULL);
480 #ifdef EXTW_USES_MOTIF
481 NOTIFY (w, extw_notify_init,
482 EXTW_TYPE_MOTIF,
483 0, 0);
484 #else
485 NOTIFY (w, extw_notify_init,
486 EXTW_TYPE_XT,
487 0, 0);
488 #endif
489 break;
490
491 case extw_notify_end:
492 end_connection (w);
493 remove_ww (w->externalClient.event_window);
494 break;
495
496 case extw_notify_set_focus:
497 #ifdef EXTW_USES_MOTIF
498 XmProcessTraversal (wid, XmTRAVERSE_CURRENT);
499 #else
500 XtSetKeyboardFocus (wid, None);
501 #endif
502 break;
503
504 }
505 }
506
507 static void Destroy(wid)
508 Widget wid;
509 {
510 ExternalClientWidget w = (ExternalClientWidget)wid;
511
512 NOTIFY(w, extw_notify_end, 0, 0, 0);
513 }
514
515 static XtGeometryResult QueryGeometry(gw, request, reply)
516 Widget gw;
517 XtWidgetGeometry *request, *reply;
518 {
519 ExternalClientWidget w = (ExternalClientWidget)gw;
520 XEvent event;
521 unsigned long request_num;
522 Display *display = XtDisplay(gw);
523 XtWidgetGeometry req = *request; /* don't modify caller's structure */
524
525 if (!XtIsRealized((Widget)w) || !w->externalClient.shell_ready)
526 return XtGeometryYes;
527
528 if (w->externalClient.dead_shell == TRUE)
529 /* The shell is sick. */
530 return XtGeometryNo;
531
532 req.sibling = None;
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);
537
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];
541
542 if (result == XtGeometryAlmost) {
543 extw_get_geometry_value(XtDisplay(gw), XtWindow(gw),
544 a_EXTW_QUERY_GEOMETRY, reply);
545 }
546 return result;
547 } else {
548 w->externalClient.dead_shell = TRUE; /* timed out; must be broken */
549 return XtGeometryNo;
550 }
551 }
552
553 static void ExternalClientFocusIn (Widget w, XEvent *event, String *params,
554 Cardinal *num_params)
555 {
556 ExternalClientWidget ecw = (ExternalClientWidget) w;
557
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);
561 }
562 #ifdef EXTW_USES_MOTIF
563 _XmPrimitiveFocusIn (w, event, params, num_params);
564 #endif
565 }
566
567 static void ExternalClientFocusOut (Widget w, XEvent *event, String *params,
568 Cardinal *num_params)
569 {
570 ExternalClientWidget ecw = (ExternalClientWidget) w;
571
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);
575 }
576 #ifdef EXTW_USES_MOTIF
577 _XmPrimitiveFocusOut(w, event, params, num_params);
578 #endif
579 }
580
581 static void ExternalClientEnter (Widget w, XEvent *event, String *params,
582 Cardinal *num_params)
583 {
584 ExternalClientWidget ecw = (ExternalClientWidget) w;
585
586 if (
587 #ifdef EXTW_USES_MOTIF
588 _XmGetFocusPolicy (w) != XmEXPLICIT &&
589 #endif
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);
594 }
595 #ifdef EXTW_USES_MOTIF
596 _XmPrimitiveEnter (w, event, params, num_params);
597 #endif
598 }
599
600 static void ExternalClientLeave (Widget w, XEvent *event, String *params,
601 Cardinal *num_params)
602 {
603 ExternalClientWidget ecw = (ExternalClientWidget) w;
604
605 if (
606 #ifdef EXTW_USES_MOTIF
607 _XmGetFocusPolicy (w) != XmEXPLICIT &&
608 #endif
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);
613 }
614 #ifdef EXTW_USES_MOTIF
615 _XmPrimitiveLeave (w, event, params, num_params);
616 #endif
617 }