1 /* External shell 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. */
23 /* This is a special Shell that is designed to use an externally-
24 provided window created by someone else (possibly another process).
25 That other window should have an associated widget of class
26 ExternalClient. The two widgets communicate with each other using
27 ClientMessage events and properties on the external window.
29 Ideally this feature should be independent of Emacs. Unfortunately
30 there are lots and lots of specifics that need to be dealt with
31 for this to work properly, and some of them can't conveniently
32 be handled within the widget's methods. Some day the code may
33 be rewritten so that the embedded-widget feature can be used by
34 any application, with appropriate entry points that are called
35 at specific points within the application.
37 This feature is similar to the OLE (Object Linking & Embedding)
38 feature provided by MS Windows.
45 #ifndef EXTERNAL_WIDGET
46 ERROR! This ought not be getting compiled if EXTERNAL_WIDGET is undefined
53 #include <X11/StringDefs.h>
54 #include "xintrinsicp.h"
55 #include <X11/Shell.h>
56 #include <X11/ShellP.h>
57 #include <X11/Vendor.h>
58 #include <X11/VendorP.h>
59 #include "ExternalShellP.h"
63 extern void emacs_Xt_handle_focus_event (XEvent *event);
66 /* Communication between this shell and the client widget:
68 Communication is through ClientMessage events with message_type
69 EXTW_NOTIFY and format 32. Both the shell and the client widget
70 communicate with each other by sending the message to the same
71 window (the "external window" below), and the data.l[0] value is
72 used to determine who sent the message.
74 The data is formatted as follows:
76 data.l[0] = who sent this message: external_shell_send (0) or
77 external_client_send (1)
78 data.l[1] = message type (see enum en_extw_notify below)
79 data.l[2-4] = data associated with this message
81 EventHandler() handles messages from the other side.
83 extw_send_notify_3() sends a message to the other side.
85 extw_send_geometry_value() is used when an XtWidgetGeometry structure
86 needs to be sent. This is too much data to fit into a
87 ClientMessage, so the data is stored in a property and then
88 extw_send_notify_3() is called.
90 extw_get_geometry_value() receives an XtWidgetGeometry structure from a
93 extw_wait_for_response() is used when a response to a sent message
94 is expected. It looks for a matching event within a
97 The particular message types are as follows:
99 1) extw_notify_init (event_window, event_mask)
101 This is sent from the shell to the client after the shell realizes
102 its EmacsFrame widget on the client's "external window". This
103 tells the client that it should start passing along events of the
104 types specified in event_mask. event_window specifies the window
105 of the EmacsFrame widget, which is a child of the client's
108 extw_notify_init (client_type)
110 When the client receives an extw_notify_init message from the
111 shell, it sends back a message of the same sort specifying the type
112 of the toolkit used by the client (Motif, generic Xt, or Xlib).
114 2) extw_notify_end ()
116 This is sent from the shell to the client when the shell's
117 EmacsFrame widget is destroyed, and tells the client to stop
118 passing events along.
120 3) extw_notify_qg (result)
122 This is sent from the client to the shell when a QueryGeometry
123 request is received on the client. The XtWidgetGeometry structure
124 specified in the QueryGeometry request is passed on in the
125 EXTW_QUERY_GEOMETRY property (of type EXTW_WIDGET_GEOMETRY) on the
126 external window. result is unused.
128 In response, the shell passes the QueryGeometry request down the
129 widget tree, and when a response is received, sends a message of
130 type extw_notify_qg back to the client, with result specifying the
131 GeometryResult value. If this value is XtGeometryAlmost, the
132 returned XtWidgetGeometry structure is stored into the same property
133 as above. [BPW is there a possible race condition here?]
135 4) extw_notify_gm (result)
137 A very similar procedure to that for extw_notify_qg is followed
138 when the shell's RootGeometryManager method is called, indicating
139 that a child widget wishes to change the shell's geometry. The
140 XtWidgetGeometry structure is stored in the EXTW_GEOMETRY_MANAGER
143 5) extw_notify_focus_in (), extw_notify_focus_out ()
145 These are sent from the client to the shell when the client gains
146 or loses the keyboard focus. It is done this way because Xt
147 maintains its own concept of keyboard focus and only the client
148 knows this information.
151 #define NOTIFY(w, type, l0, l1, l2) \
152 extw_send_notify_3(XtDisplay((Widget)(w)),\
153 (w)->externalShell.external_window, type, l0, l1, l2)
155 static void ExternalShellInitialize (Widget req, Widget new, ArgList args,
157 static void ExternalShellRealize (Widget wid, Mask *vmask, XSetWindowAttributes
159 static void ExternalShellDestroy (Widget w);
160 static void ChangeManaged (Widget wid);
161 static XtGeometryResult ExternalShellRootGeometryManager (Widget gw,
162 XtWidgetGeometry *request, XtWidgetGeometry *reply);
163 static void EventHandler (Widget wid, XtPointer closure, XEvent *event,
164 Boolean *continue_to_dispatch);
166 #ifndef DEFAULT_WM_TIMEOUT
167 # define DEFAULT_WM_TIMEOUT 5000
170 void ExternalShellUnrealize (Widget w);
172 static XtResource resources[] = {
173 #define offset(field) XtOffset(ExternalShellWidget, externalShell.field)
174 { XtNwindow, XtCWindow, XtRWindow, sizeof (Window),
175 offset (external_window), XtRImmediate, (XtPointer)0},
176 { XtNclientTimeout, XtCClientTimeout, XtRInt, sizeof(int),
177 offset(client_timeout), XtRImmediate,(XtPointer)DEFAULT_WM_TIMEOUT},
178 { XtNdeadClient, XtCDeadClient, XtRBoolean, sizeof(Boolean),
179 offset(dead_client), XtRImmediate, (XtPointer)False},
182 static CompositeClassExtensionRec compositeClassExtRec = {
185 XtCompositeExtensionVersion,
186 sizeof(CompositeClassExtensionRec),
190 static ShellClassExtensionRec shellClassExtRec = {
193 XtShellExtensionVersion,
194 sizeof(ShellClassExtensionRec),
195 ExternalShellRootGeometryManager
198 ExternalShellClassRec externalShellClassRec = {
202 /* superclass */ (WidgetClass) &shellClassRec,
203 /* class_name */ "ExternalShell",
204 /* size */ sizeof(ExternalShellRec),
205 /* Class Initializer */ NULL,
206 /* class_part_initialize*/ NULL, /* XtInheritClassPartInitialize, */
207 /* Class init'ed ? */ FALSE,
208 /* initialize */ ExternalShellInitialize,
209 /* initialize_notify */ NULL,
210 /* realize */ ExternalShellRealize,
213 /* resources */ resources,
214 /* resource_count */ XtNumber (resources),
215 /* xrm_class */ NULLQUARK,
216 /* compress_motion */ FALSE,
217 /* compress_exposure */ TRUE,
218 /* compress_enterleave*/ FALSE,
219 /* visible_interest */ TRUE,
220 /* destroy */ ExternalShellDestroy, /* XtInheritDestroy, */
221 /* resize */ XtInheritResize,
223 /* set_values */ NULL, /* XtInheritSetValues, */
224 /* set_values_hook */ NULL,
225 /* set_values_almost */ XtInheritSetValuesAlmost,
226 /* get_values_hook */ NULL,
227 /* accept_focus */ NULL,
228 /* intrinsics version */ XtVersion,
229 /* callback offsets */ NULL,
231 /* query_geometry */ NULL,
232 /* display_accelerator*/ NULL,
235 /* geometry_manager */ XtInheritGeometryManager,
236 /* change_managed */ ChangeManaged, /* XtInheritChangeManaged */
237 /* insert_child */ XtInheritInsertChild,
238 /* delete_child */ XtInheritDeleteChild,
239 /* extension */ (XtPointer)&compositeClassExtRec
241 /* extension */ (XtPointer)&shellClassExtRec
242 },{ /* ExternalShell */
247 WidgetClass externalShellWidgetClass = (WidgetClass) &externalShellClassRec;
250 ExternalShellInitialize (Widget req, Widget new, ArgList args,
253 XtAddEventHandler(new, 0,
254 TRUE, EventHandler, (XtPointer) NULL);
255 extw_initialize_atoms(XtDisplay(req));
256 extw_which_side = extw_shell_send;
260 find_managed_child (CompositeWidget w)
263 Widget *childP = w->composite.children;
265 for (i = w->composite.num_children; i; i--, childP++)
266 if (XtIsWidget(*childP) && XtIsManaged(*childP))
271 #ifndef XtCXtToolkitError
272 # define XtCXtToolkitError "XtToolkitError"
275 static void EventHandler(wid, closure, event, continue_to_dispatch)
277 XtPointer closure; /* unused */
279 Boolean *continue_to_dispatch; /* unused */
281 ExternalShellWidget w = (ExternalShellWidget) wid;
283 if(w->core.window != event->xany.window) {
284 XtAppErrorMsg(XtWidgetToApplicationContext(wid),
285 "invalidWindow","eventHandler",XtCXtToolkitError,
286 "Event with wrong window",
287 (String *)NULL, (Cardinal *)NULL);
291 if (event->type == ClientMessage &&
292 event->xclient.data.l[0] == extw_client_send &&
293 event->xclient.message_type == a_EXTW_NOTIFY)
294 switch (event->xclient.data.l[1]) {
297 /* client is alive again. */
298 w->externalShell.dead_client = False;
301 case extw_notify_qg: {
302 XtWidgetGeometry xwg, xwg_return;
303 XtGeometryResult result;
304 Widget child = find_managed_child((CompositeWidget) w);
307 extw_get_geometry_value(XtDisplay(wid), XtWindow(wid),
308 a_EXTW_QUERY_GEOMETRY, &xwg);
309 result = XtQueryGeometry(child, &xwg, &xwg_return);
311 result = XtGeometryYes;
313 extw_send_geometry_value(XtDisplay(wid), XtWindow(wid),
314 a_EXTW_QUERY_GEOMETRY, extw_notify_qg,
315 result == XtGeometryAlmost ? &xwg_return :
320 case extw_notify_focus_in: {
321 XFocusChangeEvent evnt;
324 evnt.serial = LastKnownRequestProcessed (XtDisplay (wid));
325 evnt.send_event = True;
326 evnt.display = XtDisplay (wid);
327 evnt.window = XtWindow (wid);
328 evnt.mode = NotifyNormal;
329 evnt.detail = NotifyAncestor;
331 emacs_Xt_handle_focus_event ((XEvent *) &evnt);
333 XtDispatchEvent ((XEvent *) &evnt);
338 case extw_notify_focus_out: {
339 XFocusChangeEvent evnt;
341 evnt.type = FocusOut;
342 evnt.serial = LastKnownRequestProcessed (XtDisplay (wid));
343 evnt.send_event = True;
344 evnt.display = XtDisplay (wid);
345 evnt.window = XtWindow (wid);
346 evnt.mode = NotifyNormal;
347 evnt.detail = NotifyAncestor;
349 emacs_Xt_handle_focus_event ((XEvent *) &evnt);
351 XtDispatchEvent ((XEvent *) &evnt);
356 case extw_notify_end:
357 /* frame should be destroyed. */
362 /* Lifted almost entirely from GetGeometry() in Shell.c
365 GetGeometry (Widget W, Widget child)
367 ExternalShellWidget w = (ExternalShellWidget)W;
368 int x, y, win_gravity = -1, flag;
370 Window win = w->externalShell.external_window;
374 unsigned int dummy_bd_width, dummy_depth, width, height;
376 /* determine the existing size of the window. */
377 XGetGeometry(XtDisplay(W), win, &dummy_root, &x, &y, &width,
378 &height, &dummy_bd_width, &dummy_depth);
379 w->core.width = width;
380 w->core.height = height;
383 if(w->shell.geometry != NULL) {
389 width = w->core.width;
390 height = w->core.height;
393 sprintf( def_geom, "%dx%d+%d+%d", width, height, x, y );
394 flag = XWMGeometry( XtDisplay(W),
395 XScreenNumberOfScreen(XtScreen(W)),
396 w->shell.geometry, def_geom,
397 (unsigned int)w->core.border_width,
398 &hints, &x, &y, &width, &height,
402 if (flag & XValue) w->core.x = (Position)x;
403 if (flag & YValue) w->core.y = (Position)y;
404 if (flag & WidthValue) w->core.width = (Dimension)width;
405 if (flag & HeightValue) w->core.height = (Dimension)height;
409 Cardinal num_params = 2;
410 params[0] = XtName(W);
411 params[1] = w->shell.geometry;
412 XtAppWarningMsg(XtWidgetToApplicationContext(W),
413 "badGeometry", "shellRealize", XtCXtToolkitError,
414 "Shell widget \"%s\" has an invalid geometry specification: \"%s\"",
415 params, &num_params);
421 w->shell.client_specified |= _XtShellGeometryParsed;
424 /* Lifted almost entirely from Realize() in Shell.c
426 static void ExternalShellRealize (Widget wid, Mask *vmask,
427 XSetWindowAttributes *attr)
429 ExternalShellWidget w = (ExternalShellWidget) wid;
431 Window win = w->externalShell.external_window;
435 XtErrorMsg("invalidWindow","shellRealize", XtCXtToolkitError,
436 "No external window specified for ExternalShell widget %s",
437 &wid->core.name, &count);
440 if (! (w->shell.client_specified & _XtShellGeometryParsed)) {
441 /* we'll get here only if there was no child the first
442 time we were realized. If the shell was Unrealized
443 and then re-Realized, we probably don't want to
444 re-evaluate the defaults anyway.
446 GetGeometry(wid, (Widget)NULL);
448 else if (w->core.background_pixmap == XtUnspecifiedPixmap) {
449 /* I attempt to inherit my child's background to avoid screen flash
450 * if there is latency between when I get resized and when my child
451 * is resized. Background=None is not satisfactory, as I want the
452 * user to get immediate feedback on the new dimensions (most
453 * particularly in the case of a non-reparenting wm). It is
454 * especially important to have the server clear any old cruft
455 * from the display when I am resized larger.
457 Widget *childP = w->composite.children;
459 for (i = w->composite.num_children; i; i--, childP++) {
460 if (XtIsWidget(*childP) && XtIsManaged(*childP)) {
461 if ((*childP)->core.background_pixmap
462 != XtUnspecifiedPixmap) {
463 mask &= ~(CWBackPixel);
464 mask |= CWBackPixmap;
465 attr->background_pixmap =
466 w->core.background_pixmap =
467 (*childP)->core.background_pixmap;
469 attr->background_pixel =
470 w->core.background_pixel =
471 (*childP)->core.background_pixel;
478 if(w->shell.save_under) {
480 attr->save_under = TRUE;
482 if(w->shell.override_redirect) {
483 mask |= CWOverrideRedirect;
484 attr->override_redirect = TRUE;
486 if (wid->core.width == 0 || wid->core.height == 0) {
488 XtErrorMsg("invalidDimension", "shellRealize", XtCXtToolkitError,
489 "Shell widget %s has zero width and/or height",
490 &wid->core.name, &count);
492 wid->core.window = win;
493 XChangeWindowAttributes(XtDisplay(wid), wid->core.window,
498 static void ExternalShellDestroy(wid)
501 ExternalShellWidget w = (ExternalShellWidget)wid;
503 if (XtIsRealized(wid))
504 ExternalShellUnrealize(wid);
506 NOTIFY(w, extw_notify_end, 0, 0, 0);
509 /* Invoke matching routine from superclass, but first override its
510 geometry opinions with our own routine */
512 static void ChangeManaged(wid)
515 if (!XtIsRealized (wid))
516 GetGeometry(wid, (Widget)NULL);
517 (*((ShellClassRec*)externalShellClassRec.core_class.superclass)->
518 composite_class.change_managed)(wid);
521 /* Based on RootGeometryManager() in Shell.c */
523 static XtGeometryResult ExternalShellRootGeometryManager(gw, request, reply)
525 XtWidgetGeometry *request, *reply;
527 ExternalShellWidget w = (ExternalShellWidget)gw;
528 unsigned int mask = request->request_mode;
530 int oldx, oldy, oldwidth, oldheight, oldborder_width;
531 unsigned long request_num;
532 XtWidgetGeometry req = *request; /* don't modify caller's structure */
536 oldwidth = w->core.width;
537 oldheight = w->core.height;
538 oldborder_width = w->core.border_width;
540 #define PutBackGeometry() \
541 { w->core.x = oldx; \
543 w->core.width = oldwidth; \
544 w->core.height = oldheight; \
545 w->core.border_width = oldborder_width; }
548 if (w->core.x == request->x) mask &= ~CWX;
550 w->core.x = request->x;
553 if (w->core.y == request->y) mask &= ~CWY;
554 else w->core.y = request->y;
556 if (mask & CWBorderWidth) {
557 if (w->core.border_width == request->border_width)
558 mask &= ~CWBorderWidth;
559 else w->core.border_width = request->border_width;
561 if (mask & CWWidth) {
562 if (w->core.width == request->width) mask &= ~CWWidth;
563 else w->core.width = request->width;
565 if (mask & CWHeight) {
566 if (w->core.height == request->height) mask &= ~CWHeight;
567 else w->core.height = request->height;
570 if (!XtIsRealized((Widget)w)) return XtGeometryYes;
573 req.request_mode = mask & ~CWSibling;
574 request_num = NextRequest(XtDisplay(w));
575 extw_send_geometry_value(XtDisplay(w), XtWindow(w),
576 a_EXTW_GEOMETRY_MANAGER,
577 extw_notify_gm, &req, 0);
579 if (w->externalShell.dead_client == TRUE) {
580 /* The client is sick. Refuse the request.
581 * If the client recovers and decides to honor the
582 * request, it will be handled by Shell's EventHandler().
588 if (extw_wait_for_response(gw, &event, request_num, extw_notify_gm,
589 w->externalShell.client_timeout)) {
590 XtGeometryResult result = (XtGeometryResult) event.xclient.data.l[2];
592 if (result != XtGeometryYes)
594 if (result == XtGeometryAlmost) {
595 extw_get_geometry_value(XtDisplay(w), XtWindow(w),
596 a_EXTW_GEOMETRY_MANAGER, reply);
600 w->externalShell.dead_client = TRUE; /* timed out; must be broken */
604 #undef PutBackGeometry
608 hack_event_masks_1 (Display *display, Window w, int this_window_propagate)
610 Window root, parent, *children;
611 unsigned int nchildren;
614 if (!XQueryTree (display, w, &root, &parent, &children, &nchildren))
616 for (i=0; i<nchildren; i++)
617 hack_event_masks_1 (display, children[i], 1);
621 XWindowAttributes xwa;
622 XSetWindowAttributes xswa;
623 if (XGetWindowAttributes (display, w, &xwa)) {
624 xswa.event_mask = xwa.your_event_mask & ~KeyPressMask;
625 if (this_window_propagate)
626 xswa.do_not_propagate_mask = xwa.do_not_propagate_mask & ~KeyPressMask;
627 XChangeWindowAttributes (display, w, CWEventMask, &xswa);
632 /* fix all event masks on all subwindows of the specified window so that
633 all key presses in any subwindow filter up to the specified window.
635 We have to do this cruftiness with external widgets so that we don't
636 step on Motif's concept of keyboard focus. (Due to the nature of
637 Xt and Motif, X's idea of who gets the keyboard events may not jive
638 with Xt's idea of same, and Xt redirects the events to the proper
639 window. This occurs on the client side and we have no knowledge
640 of it, so we have to rely on a SendEvent from the client side to
641 receive our keyboard events.)
645 hack_event_masks (Display *display, Window w)
647 hack_event_masks_1 (display, w, 0);
650 /* external entry points */
653 ExternalShellReady (Widget w, Window win, long event_mask)
655 ExternalShellWidget ew = (ExternalShellWidget) w;
657 unsigned long request_num;
659 request_num = NextRequest(XtDisplay(w));
660 NOTIFY(ew, extw_notify_init, (long) win, event_mask, 0);
661 if (extw_wait_for_response(w, &event, request_num, extw_notify_init,
662 ew->externalShell.client_timeout))
664 /* Xt/Xm extw's have more elaborate focus needs than mere
667 Rather independently, they *don't* need the
668 ConfigureNotify event, having fixed up the window size in
669 ChangeManaged, above, but Xlib extw's do need this.
671 ew->externalShell.client_type = event.xclient.data.l[2];
672 if (ew->externalShell.client_type != EXTW_TYPE_XLIB)
674 hack_event_masks (XtDisplay (w), XtWindow (w));
679 XWindowAttributes xwa;
680 ev.type = ConfigureNotify;
681 ev.display = XtDisplay (w);
682 ev.event = ev.window = XtWindow (w);
683 XGetWindowAttributes (ev.display, ev.window, &xwa);
684 ev.x = xwa.x; ev.y = xwa.y;
685 ev.width = xwa.width; ev.height = xwa.height;
686 ev.border_width = xwa.border_width;
688 ev.override_redirect = xwa.override_redirect;
689 XtDispatchEvent ((XEvent *) &ev);
698 ExternalShellSetFocus (Widget wid)
700 ExternalShellWidget w = (ExternalShellWidget) wid;
702 NOTIFY(w, extw_notify_set_focus, 0, 0, 0);
705 extern void _XtUnregisterWindow (Window, Widget);
708 ExternalShellUnrealize (Widget w)
710 #if (XT_REVISION > 5)
711 XtUnregisterDrawable (XtDisplay (w), w->core.window);
713 extern void _XtUnregisterWindow (Window, Widget);
714 _XtUnregisterWindow (w->core.window, w);