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,
175 XtRWindow, sizeof (Window),
176 offset (external_window), XtRImmediate, (XtPointer)0 },
177 { XtNclientTimeout, XtCClientTimeout,
178 XtRInt, sizeof (int),
179 offset(client_timeout), XtRImmediate,(XtPointer)DEFAULT_WM_TIMEOUT },
180 { XtNdeadClient, XtCDeadClient,
181 XtRBoolean, sizeof (Boolean),
182 offset(dead_client), XtRImmediate, (XtPointer)False },
185 static CompositeClassExtensionRec compositeClassExtRec = {
188 XtCompositeExtensionVersion,
189 sizeof (CompositeClassExtensionRec),
193 static ShellClassExtensionRec shellClassExtRec = {
196 XtShellExtensionVersion,
197 sizeof (ShellClassExtensionRec),
198 ExternalShellRootGeometryManager
201 ExternalShellClassRec externalShellClassRec = {
205 /* superclass */ (WidgetClass) &shellClassRec,
206 /* class_name */ "ExternalShell",
207 /* size */ sizeof (ExternalShellRec),
208 /* Class Initializer */ NULL,
209 /* class_part_initialize*/ NULL, /* XtInheritClassPartInitialize, */
210 /* Class init'ed ? */ FALSE,
211 /* initialize */ ExternalShellInitialize,
212 /* initialize_notify */ NULL,
213 /* realize */ ExternalShellRealize,
216 /* resources */ resources,
217 /* resource_count */ XtNumber (resources),
218 /* xrm_class */ NULLQUARK,
219 /* compress_motion */ FALSE,
220 /* compress_exposure */ TRUE,
221 /* compress_enterleave*/ FALSE,
222 /* visible_interest */ TRUE,
223 /* destroy */ ExternalShellDestroy, /* XtInheritDestroy, */
224 /* resize */ XtInheritResize,
226 /* set_values */ NULL, /* XtInheritSetValues, */
227 /* set_values_hook */ NULL,
228 /* set_values_almost */ XtInheritSetValuesAlmost,
229 /* get_values_hook */ NULL,
230 /* accept_focus */ NULL,
231 /* intrinsics version */ XtVersion,
232 /* callback offsets */ NULL,
234 /* query_geometry */ NULL,
235 /* display_accelerator*/ NULL,
238 /* geometry_manager */ XtInheritGeometryManager,
239 /* change_managed */ ChangeManaged, /* XtInheritChangeManaged */
240 /* insert_child */ XtInheritInsertChild,
241 /* delete_child */ XtInheritDeleteChild,
242 /* extension */ (XtPointer)&compositeClassExtRec
244 /* extension */ (XtPointer)&shellClassExtRec
245 },{ /* ExternalShell */
250 WidgetClass externalShellWidgetClass = (WidgetClass) &externalShellClassRec;
253 ExternalShellInitialize (Widget req, Widget new, ArgList args,
256 XtAddEventHandler(new, 0,
257 TRUE, EventHandler, (XtPointer) NULL);
258 extw_initialize_atoms(XtDisplay(req));
259 extw_which_side = extw_shell_send;
263 find_managed_child (CompositeWidget w)
266 Widget *childP = w->composite.children;
268 for (i = w->composite.num_children; i; i--, childP++)
269 if (XtIsWidget(*childP) && XtIsManaged(*childP))
274 #ifndef XtCXtToolkitError
275 # define XtCXtToolkitError "XtToolkitError"
278 static void EventHandler(wid, closure, event, continue_to_dispatch)
280 XtPointer closure; /* unused */
282 Boolean *continue_to_dispatch; /* unused */
284 ExternalShellWidget w = (ExternalShellWidget) wid;
286 if(w->core.window != event->xany.window) {
287 XtAppErrorMsg(XtWidgetToApplicationContext(wid),
288 "invalidWindow","eventHandler",XtCXtToolkitError,
289 "Event with wrong window",
290 (String *)NULL, (Cardinal *)NULL);
294 if (event->type == ClientMessage &&
295 event->xclient.data.l[0] == extw_client_send &&
296 event->xclient.message_type == a_EXTW_NOTIFY)
297 switch (event->xclient.data.l[1]) {
300 /* client is alive again. */
301 w->externalShell.dead_client = False;
304 case extw_notify_qg: {
305 XtWidgetGeometry xwg, xwg_return;
306 XtGeometryResult result;
307 Widget child = find_managed_child((CompositeWidget) w);
310 extw_get_geometry_value(XtDisplay(wid), XtWindow(wid),
311 a_EXTW_QUERY_GEOMETRY, &xwg);
312 result = XtQueryGeometry(child, &xwg, &xwg_return);
314 result = XtGeometryYes;
316 extw_send_geometry_value(XtDisplay(wid), XtWindow(wid),
317 a_EXTW_QUERY_GEOMETRY, extw_notify_qg,
318 result == XtGeometryAlmost ? &xwg_return :
323 case extw_notify_focus_in: {
324 XFocusChangeEvent evnt;
327 evnt.serial = LastKnownRequestProcessed (XtDisplay (wid));
328 evnt.send_event = True;
329 evnt.display = XtDisplay (wid);
330 evnt.window = XtWindow (wid);
331 evnt.mode = NotifyNormal;
332 evnt.detail = NotifyAncestor;
334 emacs_Xt_handle_focus_event ((XEvent *) &evnt);
336 XtDispatchEvent ((XEvent *) &evnt);
341 case extw_notify_focus_out: {
342 XFocusChangeEvent evnt;
344 evnt.type = FocusOut;
345 evnt.serial = LastKnownRequestProcessed (XtDisplay (wid));
346 evnt.send_event = True;
347 evnt.display = XtDisplay (wid);
348 evnt.window = XtWindow (wid);
349 evnt.mode = NotifyNormal;
350 evnt.detail = NotifyAncestor;
352 emacs_Xt_handle_focus_event ((XEvent *) &evnt);
354 XtDispatchEvent ((XEvent *) &evnt);
359 case extw_notify_end:
360 /* frame should be destroyed. */
365 /* Lifted almost entirely from GetGeometry() in Shell.c
368 GetGeometry (Widget W, Widget child)
370 ExternalShellWidget w = (ExternalShellWidget)W;
371 int x, y, win_gravity = -1, flag;
373 Window win = w->externalShell.external_window;
377 unsigned int dummy_bd_width, dummy_depth, width, height;
379 /* determine the existing size of the window. */
380 XGetGeometry(XtDisplay(W), win, &dummy_root, &x, &y, &width,
381 &height, &dummy_bd_width, &dummy_depth);
382 w->core.width = width;
383 w->core.height = height;
386 if(w->shell.geometry != NULL) {
392 width = w->core.width;
393 height = w->core.height;
396 sprintf( def_geom, "%dx%d+%d+%d", width, height, x, y );
397 flag = XWMGeometry( XtDisplay(W),
398 XScreenNumberOfScreen(XtScreen(W)),
399 w->shell.geometry, def_geom,
400 (unsigned int)w->core.border_width,
401 &hints, &x, &y, &width, &height,
405 if (flag & XValue) w->core.x = (Position)x;
406 if (flag & YValue) w->core.y = (Position)y;
407 if (flag & WidthValue) w->core.width = (Dimension)width;
408 if (flag & HeightValue) w->core.height = (Dimension)height;
412 Cardinal num_params = 2;
413 params[0] = XtName(W);
414 params[1] = w->shell.geometry;
415 XtAppWarningMsg(XtWidgetToApplicationContext(W),
416 "badGeometry", "shellRealize", XtCXtToolkitError,
417 "Shell widget \"%s\" has an invalid geometry specification: \"%s\"",
418 params, &num_params);
424 w->shell.client_specified |= _XtShellGeometryParsed;
427 /* Lifted almost entirely from Realize() in Shell.c
429 static void ExternalShellRealize (Widget wid, Mask *vmask,
430 XSetWindowAttributes *attr)
432 ExternalShellWidget w = (ExternalShellWidget) wid;
434 Window win = w->externalShell.external_window;
438 XtErrorMsg("invalidWindow","shellRealize", XtCXtToolkitError,
439 "No external window specified for ExternalShell widget %s",
440 &wid->core.name, &count);
443 if (! (w->shell.client_specified & _XtShellGeometryParsed)) {
444 /* we'll get here only if there was no child the first
445 time we were realized. If the shell was Unrealized
446 and then re-Realized, we probably don't want to
447 re-evaluate the defaults anyway.
449 GetGeometry(wid, (Widget)NULL);
451 else if (w->core.background_pixmap == XtUnspecifiedPixmap) {
452 /* I attempt to inherit my child's background to avoid screen flash
453 * if there is latency between when I get resized and when my child
454 * is resized. Background=None is not satisfactory, as I want the
455 * user to get immediate feedback on the new dimensions (most
456 * particularly in the case of a non-reparenting wm). It is
457 * especially important to have the server clear any old cruft
458 * from the display when I am resized larger.
460 Widget *childP = w->composite.children;
462 for (i = w->composite.num_children; i; i--, childP++) {
463 if (XtIsWidget(*childP) && XtIsManaged(*childP)) {
464 if ((*childP)->core.background_pixmap
465 != XtUnspecifiedPixmap) {
466 mask &= ~(CWBackPixel);
467 mask |= CWBackPixmap;
468 attr->background_pixmap =
469 w->core.background_pixmap =
470 (*childP)->core.background_pixmap;
472 attr->background_pixel =
473 w->core.background_pixel =
474 (*childP)->core.background_pixel;
481 if(w->shell.save_under) {
483 attr->save_under = TRUE;
485 if(w->shell.override_redirect) {
486 mask |= CWOverrideRedirect;
487 attr->override_redirect = TRUE;
489 if (wid->core.width == 0 || wid->core.height == 0) {
491 XtErrorMsg("invalidDimension", "shellRealize", XtCXtToolkitError,
492 "Shell widget %s has zero width and/or height",
493 &wid->core.name, &count);
495 wid->core.window = win;
496 XChangeWindowAttributes(XtDisplay(wid), wid->core.window,
501 static void ExternalShellDestroy(wid)
504 ExternalShellWidget w = (ExternalShellWidget)wid;
506 if (XtIsRealized(wid))
507 ExternalShellUnrealize(wid);
509 NOTIFY(w, extw_notify_end, 0, 0, 0);
512 /* Invoke matching routine from superclass, but first override its
513 geometry opinions with our own routine */
515 static void ChangeManaged(wid)
518 if (!XtIsRealized (wid))
519 GetGeometry(wid, (Widget)NULL);
520 (*((ShellClassRec*)externalShellClassRec.core_class.superclass)->
521 composite_class.change_managed)(wid);
524 /* Based on RootGeometryManager() in Shell.c */
526 static XtGeometryResult ExternalShellRootGeometryManager(gw, request, reply)
528 XtWidgetGeometry *request, *reply;
530 ExternalShellWidget w = (ExternalShellWidget)gw;
531 unsigned int mask = request->request_mode;
533 int oldx, oldy, oldwidth, oldheight, oldborder_width;
534 unsigned long request_num;
535 XtWidgetGeometry req = *request; /* don't modify caller's structure */
539 oldwidth = w->core.width;
540 oldheight = w->core.height;
541 oldborder_width = w->core.border_width;
543 #define PutBackGeometry() \
544 { w->core.x = oldx; \
546 w->core.width = oldwidth; \
547 w->core.height = oldheight; \
548 w->core.border_width = oldborder_width; }
551 if (w->core.x == request->x) mask &= ~CWX;
553 w->core.x = request->x;
556 if (w->core.y == request->y) mask &= ~CWY;
557 else w->core.y = request->y;
559 if (mask & CWBorderWidth) {
560 if (w->core.border_width == request->border_width)
561 mask &= ~CWBorderWidth;
562 else w->core.border_width = request->border_width;
564 if (mask & CWWidth) {
565 if (w->core.width == request->width) mask &= ~CWWidth;
566 else w->core.width = request->width;
568 if (mask & CWHeight) {
569 if (w->core.height == request->height) mask &= ~CWHeight;
570 else w->core.height = request->height;
573 if (!XtIsRealized((Widget)w)) return XtGeometryYes;
576 req.request_mode = mask & ~CWSibling;
577 request_num = NextRequest(XtDisplay(w));
578 extw_send_geometry_value(XtDisplay(w), XtWindow(w),
579 a_EXTW_GEOMETRY_MANAGER,
580 extw_notify_gm, &req, 0);
582 if (w->externalShell.dead_client == TRUE) {
583 /* The client is sick. Refuse the request.
584 * If the client recovers and decides to honor the
585 * request, it will be handled by Shell's EventHandler().
591 if (extw_wait_for_response(gw, &event, request_num, extw_notify_gm,
592 w->externalShell.client_timeout)) {
593 XtGeometryResult result = (XtGeometryResult) event.xclient.data.l[2];
595 if (result != XtGeometryYes)
597 if (result == XtGeometryAlmost) {
598 extw_get_geometry_value(XtDisplay(w), XtWindow(w),
599 a_EXTW_GEOMETRY_MANAGER, reply);
603 w->externalShell.dead_client = TRUE; /* timed out; must be broken */
607 #undef PutBackGeometry
611 hack_event_masks_1 (Display *display, Window w, int this_window_propagate)
613 Window root, parent, *children;
614 unsigned int nchildren;
617 if (!XQueryTree (display, w, &root, &parent, &children, &nchildren))
619 for (i=0; i<nchildren; i++)
620 hack_event_masks_1 (display, children[i], 1);
624 XWindowAttributes xwa;
625 XSetWindowAttributes xswa;
626 if (XGetWindowAttributes (display, w, &xwa)) {
627 xswa.event_mask = xwa.your_event_mask & ~KeyPressMask;
628 if (this_window_propagate)
629 xswa.do_not_propagate_mask = xwa.do_not_propagate_mask & ~KeyPressMask;
630 XChangeWindowAttributes (display, w, CWEventMask, &xswa);
635 /* fix all event masks on all subwindows of the specified window so that
636 all key presses in any subwindow filter up to the specified window.
638 We have to do this cruftiness with external widgets so that we don't
639 step on Motif's concept of keyboard focus. (Due to the nature of
640 Xt and Motif, X's idea of who gets the keyboard events may not jive
641 with Xt's idea of same, and Xt redirects the events to the proper
642 window. This occurs on the client side and we have no knowledge
643 of it, so we have to rely on a SendEvent from the client side to
644 receive our keyboard events.)
648 hack_event_masks (Display *display, Window w)
650 hack_event_masks_1 (display, w, 0);
653 /* external entry points */
656 ExternalShellReady (Widget w, Window win, long event_mask)
658 ExternalShellWidget ew = (ExternalShellWidget) w;
660 unsigned long request_num;
662 request_num = NextRequest(XtDisplay(w));
663 NOTIFY(ew, extw_notify_init, (long) win, event_mask, 0);
664 if (extw_wait_for_response(w, &event, request_num, extw_notify_init,
665 ew->externalShell.client_timeout))
667 /* Xt/Xm extw's have more elaborate focus needs than mere
670 Rather independently, they *don't* need the
671 ConfigureNotify event, having fixed up the window size in
672 ChangeManaged, above, but Xlib extw's do need this.
674 ew->externalShell.client_type = event.xclient.data.l[2];
675 if (ew->externalShell.client_type != EXTW_TYPE_XLIB)
677 hack_event_masks (XtDisplay (w), XtWindow (w));
682 XWindowAttributes xwa;
683 ev.type = ConfigureNotify;
684 ev.display = XtDisplay (w);
685 ev.event = ev.window = XtWindow (w);
686 XGetWindowAttributes (ev.display, ev.window, &xwa);
687 ev.x = xwa.x; ev.y = xwa.y;
688 ev.width = xwa.width; ev.height = xwa.height;
689 ev.border_width = xwa.border_width;
691 ev.override_redirect = xwa.override_redirect;
692 XtDispatchEvent ((XEvent *) &ev);
701 ExternalShellSetFocus (Widget wid)
703 ExternalShellWidget w = (ExternalShellWidget) wid;
705 NOTIFY(w, extw_notify_set_focus, 0, 0, 0);
708 extern void _XtUnregisterWindow (Window, Widget);
711 ExternalShellUnrealize (Widget w)
713 #if (XT_REVISION > 5)
714 XtUnregisterDrawable (XtDisplay (w), w->core.window);
716 extern void _XtUnregisterWindow (Window, Widget);
717 _XtUnregisterWindow (w->core.window, w);