/* $XConsortium: GrabShell.c /main/9 1996/08/15 17:12:04 pascale $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ #include "XmI.h" #include #include #include #include #include #include #include #include #include #include #include "ColorI.h" #include "MenuShellI.h" #include "PixConvI.h" #include "UniqueEvnI.h" /* Warning messages */ #define default_translations _XmGrabShell_translations #define Events (EnterWindowMask | LeaveWindowMask | \ ButtonPressMask | ButtonReleaseMask) /******** Static Function Declarations ********/ static void BtnUp (Widget grabshell, XEvent *event, String *params, Cardinal *num_params); static void BtnDown (Widget grabshell, XEvent *event, String *params, Cardinal *num_params); static void Popdown (Widget grabshell, XEvent *event, String *params, Cardinal *num_params); static void ClassPartInitialize (WidgetClass wc); static void Initialize (Widget req, Widget new_w, ArgList args, Cardinal *num_args); static Boolean SetValues (Widget cw, Widget rw, Widget nw, ArgList args, Cardinal *num_args); static void Resize (Widget wid); static void ChangeManaged (Widget w); static XtGeometryResult GeometryManager( Widget wid, XtWidgetGeometry *request, XtWidgetGeometry *reply) ; static void Destroy (Widget wid); static void MapNotifyHandler(Widget shell, XtPointer client_data, XEvent *, Boolean *); static void _XmFastExpose (Widget widget); static void DrawBorder (Widget widget); static void DoLayout (Widget gs); static void GSAllowEvents(Widget gs, int, Time); static int IgnoreXErrors(Display *, XErrorEvent *); /******** End Static Function Declarations ********/ static XtActionsRec actionsList[] = { { "GrabShellBtnDown", BtnDown }, { "GrabShellBtnUp", BtnUp }, { "GrabShellPopdown", Popdown } }; #define Offset(field) (XtOffsetOf(XmGrabShellRec, field)) static XtResource resources[] = { { XmNallowShellResize, XmCAllowShellResize, XmRBoolean, sizeof(Boolean), Offset(shell.allow_shell_resize), XtRImmediate, (XtPointer)TRUE }, { XmNbackground, XmCBackground, XmRPixel, sizeof (Pixel), Offset(core.background_pixel), XmRCallProc, (XtPointer) _XmBackgroundColorDefault }, { XmNoverrideRedirect, XmCOverrideRedirect, XmRBoolean, sizeof(Boolean), Offset(shell.override_redirect), XtRImmediate, (XtPointer)TRUE }, { XmNsaveUnder, XmCSaveUnder, XmRBoolean, sizeof(Boolean), Offset(shell.save_under), XtRImmediate, (XtPointer)FALSE }, { XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, sizeof(Dimension), Offset(grab_shell.shadow_thickness), XmRImmediate, (XtPointer)2 }, { XmNtransient, XmCTransient, XmRBoolean, sizeof(Boolean), Offset(wm_shell.transient), XtRImmediate, (XtPointer)TRUE }, { XmNwaitForWm, XmCWaitForWm, XmRBoolean, sizeof(Boolean), Offset(wm_shell.wait_for_wm), XtRImmediate, (XtPointer)FALSE }, { XmNtopShadowColor, XmCTopShadowColor, XmRPixel, sizeof(Pixel), Offset(grab_shell.top_shadow_color), XmRCallProc, (XtPointer) _XmTopShadowColorDefault }, { XmNtopShadowPixmap, XmCTopShadowPixmap, XmRNoScalingDynamicPixmap, sizeof(Pixmap), Offset(grab_shell.top_shadow_pixmap), XmRCallProc, (XtPointer) _XmTopShadowPixmapDefault }, { XmNbottomShadowColor, XmCBottomShadowColor, XmRPixel, sizeof(Pixel), Offset(grab_shell.bottom_shadow_color), XmRCallProc, (XtPointer) _XmBottomShadowColorDefault }, { XmNbottomShadowPixmap, XmCBottomShadowPixmap, XmRNoScalingDynamicPixmap, sizeof(Pixmap), Offset(grab_shell.bottom_shadow_pixmap), XmRImmediate, (XtPointer) XmUNSPECIFIED_PIXMAP }, { XmNgrabStyle, XmCGrabStyle, XmRInt, sizeof(int), Offset(grab_shell.grab_style), XmRImmediate, (XtPointer) GrabModeAsync }, { XmNownerEvents, XmCOwnerEvents, XmRBoolean, sizeof(Boolean), Offset(grab_shell.owner_events), XmRImmediate, (XtPointer) FALSE } }; #undef Offset externaldef(xmgrabshellclassrec) XmGrabShellClassRec xmGrabShellClassRec = { { /* core class fields */ (WidgetClass) &vendorShellClassRec, /* superclass */ "XmGrabShell", /* class_name */ sizeof (XmGrabShellWidgetRec), /* widget_size */ NULL, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ (XtArgsProc)NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actionsList, /* actions */ XtNumber(actionsList), /* num_actions */ resources, /* resource list */ XtNumber(resources), /* resource_count */ NULLQUARK, /* xrm_class */ True, /* compress_motion */ XtExposeCompressMaximal, /* compress_exposure */ TRUE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ NULL, /* expose */ SetValues, /* set_values */ (XtArgsFunc)NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ (XtArgsProc)NULL, /* get_values_hook */ (XtAcceptFocusProc)NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ default_translations, /* tm_table */ (XtGeometryHandler)NULL, /* query_geometry */ (XtStringProc)NULL, /* display_accelerator */ NULL, /* extension */ }, { /* composite class fields */ GeometryManager, /* geometry_manager */ ChangeManaged, /* change_managed */ XtInheritInsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL, /* extension */ }, { /* shell class fields */ NULL, /* extension */ }, { /* wmshell class fields */ NULL, /* extension */ }, { /* vendor shell class fields */ NULL, /* extension */ }, { /* grabshell class fields */ NULL, /* extension */ }, }; externaldef(xmgrabshellwidgetclass) WidgetClass xmGrabShellWidgetClass = (WidgetClass) &xmGrabShellClassRec; /* ------------- WIDGET CLASS METHODS ---------- */ /* * Initialize() */ /*ARGSUSED*/ static void Initialize(Widget req, /* unused */ Widget new_w, ArgList args, /* unused */ Cardinal *num_args) /* unused */ { XmGrabShellWidget grabsh = (XmGrabShellWidget)new_w; XtAddEventHandler(new_w, StructureNotifyMask, False, MapNotifyHandler, NULL); grabsh->grab_shell.unpost_time = (Time) -1; grabsh->grab_shell.cursor = None; grabsh->grab_shell.top_shadow_GC = _XmGetPixmapBasedGC (new_w, grabsh->grab_shell.top_shadow_color, grabsh->core.background_pixel, grabsh->grab_shell.top_shadow_pixmap); grabsh->grab_shell.bottom_shadow_GC = _XmGetPixmapBasedGC (new_w, grabsh->grab_shell.bottom_shadow_color, grabsh->core.background_pixel, grabsh->grab_shell.bottom_shadow_pixmap); /* CR 6723: The BtnUp event may arrive before MapNotify. */ grabsh->grab_shell.post_time = XtLastTimestampProcessed(XtDisplay(new_w)); /* CR 9920: Popdown may be requested before MapNotify. */ grabsh->grab_shell.mapped = False; } /* * ClassPartInitialize() * Set up the fast subclassing. */ static void ClassPartInitialize(WidgetClass wc) { _XmFastSubclassInit (wc, XmGRAB_SHELL_BIT); } /* * SetValues() */ /*ARGSUSED*/ static Boolean SetValues(Widget cw, Widget rw, /* unused */ Widget nw, ArgList args, /* unused */ Cardinal *num_args) /* unused */ { XmGrabShellWidget new_w = (XmGrabShellWidget) nw; XmGrabShellWidget old_w = (XmGrabShellWidget) cw; Boolean redisplay = FALSE; if (old_w->grab_shell.shadow_thickness != new_w->grab_shell.shadow_thickness) { if (XtIsRealized(nw)) { DoLayout(nw); redisplay = TRUE; } } if ((old_w->grab_shell.top_shadow_color != new_w->grab_shell.top_shadow_color) || (old_w->grab_shell.top_shadow_pixmap != new_w->grab_shell.top_shadow_pixmap)) { XtReleaseGC (nw, new_w->grab_shell.top_shadow_GC); new_w->grab_shell.top_shadow_GC = _XmGetPixmapBasedGC (nw, new_w->grab_shell.top_shadow_color, new_w->core.background_pixel, new_w->grab_shell.top_shadow_pixmap); redisplay = TRUE; } if ((old_w->grab_shell.bottom_shadow_color != new_w->grab_shell.bottom_shadow_color) || (old_w->grab_shell.bottom_shadow_pixmap != new_w->grab_shell.bottom_shadow_pixmap)) { XtReleaseGC (nw, new_w->grab_shell.bottom_shadow_GC); new_w->grab_shell.bottom_shadow_GC = _XmGetPixmapBasedGC (nw, new_w->grab_shell.bottom_shadow_color, new_w->core.background_pixel, new_w->grab_shell.bottom_shadow_pixmap); redisplay = TRUE; } return redisplay; } /* * PopupCB() * Grabs. */ /*ARGSUSED*/ static void MapNotifyHandler(Widget shell, XtPointer client_data, XEvent *event, Boolean *cont) { XmGrabShellWidget grabshell = (XmGrabShellWidget)shell; Time time; XErrorHandler old_handler; /* Only handles map events */ if (event -> type != MapNotify) return; /* CR 9920: Popdown may be called before MapNotify. */ grabshell->grab_shell.mapped = True; if (!(time = XtLastTimestampProcessed(XtDisplay(shell)))) time = CurrentTime; if (grabshell->grab_shell.cursor == None) grabshell->grab_shell.cursor = XCreateFontCursor (XtDisplay(grabshell), XC_arrow); _XmFastExpose(shell); (void) XtGrabKeyboard(shell, grabshell -> grab_shell.owner_events, grabshell -> grab_shell.grab_style, GrabModeAsync, time); (void) XtGrabPointer(shell, grabshell -> grab_shell.owner_events, Events, grabshell -> grab_shell.grab_style, GrabModeAsync, None, grabshell->grab_shell.cursor, time); GSAllowEvents(shell, SyncPointer, time); /* Fix focus to shell */ XGetInputFocus(XtDisplay(shell), &grabshell->grab_shell.old_focus, &grabshell->grab_shell.old_revert_to); old_handler = XSetErrorHandler(IgnoreXErrors); XSetInputFocus(XtDisplay(shell), XtWindow(shell), RevertToParent, time); XSync(XtDisplay(shell), False); XSetErrorHandler(old_handler); } /* * For BtnUp and BtnDown events we need to decide whether to * popdown the grabshell. We "see" these if the user presses * outside the shell. * * To decide, we call the XmNhasInterestCB to see if our poster * wants to handle the event. If our poster does, we call * XAllowEvents with REPLAY to get the event to the poster, otherwise * we Popdown() * */ static void BtnUp (Widget w, XEvent *event, String *params, Cardinal *num_params) { XmGrabShellWidget grabshell = (XmGrabShellWidget) w; int delta; /* Handle click to post we then ignore the event if it occured within the click to post time */ delta = event -> xbutton.time - grabshell -> grab_shell.post_time; if (delta <= XtGetMultiClickTime(XtDisplay(w))) { GSAllowEvents(w, SyncPointer, event -> xbutton.time); return; } Popdown(w, event, params, num_params); } static void BtnDown (Widget grabshell, XEvent *event, String *params, Cardinal *num_params) { int x, y; Window win; /* Ignore modal cascade replay of event */ if (! _XmIsEventUnique(event)) return; /* Move to grabshell's coordinate system */ XTranslateCoordinates(XtDisplay(grabshell), event -> xbutton.window, XtWindow(grabshell), event -> xbutton.x, event -> xbutton.y, &x, &y, &win); /* Popdown if outside the shell */ if (x >= 0 && y >= 0 && x <= XtWidth(grabshell) && y <= XtHeight(grabshell)) { GSAllowEvents(grabshell, SyncPointer, event -> xbutton.time); } else { Popdown(grabshell, event, params, num_params); } } /* * Popdown() * Popdown a GrabShell widget, also flag it's child as unmanaged. */ /*ARGSUSED*/ static void Popdown(Widget shell, XEvent *event, /* unused */ String *params, Cardinal *num_params) { XmScreen screen = (XmScreen) XmGetXmScreen(XtScreen(shell)); XmGrabShellWidget grabshell = (XmGrabShellWidget)shell; Time time; /* Record for replay detection */ if (event && (event->type == ButtonPress || event->type == ButtonRelease)) { grabshell->grab_shell.unpost_time = event->xbutton.time; } if (!(time = XtLastTimestampProcessed(XtDisplay(shell)))) time = CurrentTime; /* CR 9920: Popdown may be called before MapNotify. */ if (grabshell->shell.popped_up && grabshell->grab_shell.mapped) { XErrorHandler old_handler; if (screen -> screen.unpostBehavior == XmUNPOST_AND_REPLAY) GSAllowEvents(shell, ReplayPointer, event ? event->xbutton.time : time); XtUngrabPointer(shell, time); XtUngrabKeyboard(shell, time); _XmPopdown(shell); /* Reset focus to old holder */ old_handler = XSetErrorHandler(IgnoreXErrors); if (time != CurrentTime) time = time - 1; /* Avoid race in wm */ XSetInputFocus(XtDisplay(shell), grabshell->grab_shell.old_focus, grabshell->grab_shell.old_revert_to, time); XSync(XtDisplay(shell), False); XSetErrorHandler(old_handler); } grabshell->grab_shell.mapped = False; } /* * This only calls allow events if we have a sync grab. */ static void GSAllowEvents(Widget gs, int mode, Time time) { XmGrabShellWidget grabshell = (XmGrabShellWidget) gs; if (grabshell -> grab_shell.grab_style == GrabModeSync) { XAllowEvents(XtDisplay(gs), mode, time); } } /* * Destroy() */ static void Destroy(Widget widg) { XmGrabShellWidget grabshell = (XmGrabShellWidget) widg; if (grabshell->grab_shell.cursor != None) XFreeCursor(XtDisplay(widg), grabshell->grab_shell.cursor); } /* * DoLayout() */ static void DoLayout(Widget wid) { XmGrabShellWidget gs = (XmGrabShellWidget)wid; if (XtIsManaged(gs->composite.children[0])) { Widget childwid = gs->composite.children[0]; Position offset = (gs->grab_shell.shadow_thickness + childwid->core.border_width); int cw = ((int) gs->core.width) - 2 * offset; int ch = ((int) gs->core.height) - 2 * offset; Dimension childW = MAX(1, cw); Dimension childH = MAX(1, ch); XmeConfigureObject (childwid, offset, offset, childW, childH, childwid->core.border_width); } } /************************************************************************ * * GeometryManager * ************************************************************************/ /*ARGSUSED*/ static XtGeometryResult GeometryManager( Widget wid, XtWidgetGeometry *request, XtWidgetGeometry *reply ) /* unused */ { XmGrabShellWidget gs = (XmGrabShellWidget) XtParent(wid); XtWidgetGeometry modified; int bw; XtGeometryResult ret_val; /* Copy the existing request */ modified = *request; bw = XtBorderWidth(wid); /* Add shell's shadow thickness and child's borderwidth */ modified.width += 2*bw + 2*gs->grab_shell.shadow_thickness; modified.height += 2*bw + 2*gs->grab_shell.shadow_thickness; _XmProcessLock(); /* Send to vendor shell for final */ ret_val = ((VendorShellClassRec *) vendorShellWidgetClass) -> composite_class.geometry_manager(wid,&modified,reply); _XmProcessUnlock(); return ret_val; } /* * ChangeManaged() */ static void ChangeManaged(Widget wid) { XmGrabShellWidget gs = (XmGrabShellWidget)wid; ShellWidget shell = (ShellWidget)wid; Dimension bw = 0; XtWidgetGeometry pref, mygeom, replygeom; XtGeometryResult result; Widget child; mygeom.request_mode = 0; if (gs->composite.num_children) { child = gs->composite.children[0]; if (XtIsManaged(child)) { /* Get child's preferred size */ result = XtQueryGeometry(child, NULL, &pref); /* Take whatever they want */ if (pref.request_mode & CWWidth) { mygeom.width = pref.width; mygeom.request_mode |= CWWidth; } if (pref.request_mode & CWHeight) { mygeom.height = pref.height; mygeom.request_mode |= CWHeight; } if (pref.request_mode & CWBorderWidth) bw = pref.border_width; else bw = child->core.border_width; } } mygeom.width += 2*bw + 2*gs->grab_shell.shadow_thickness; mygeom.height += 2*bw + 2*gs->grab_shell.shadow_thickness; result = XtMakeGeometryRequest((Widget)shell, &mygeom, &replygeom); switch (result) { case XtGeometryAlmost: XtMakeGeometryRequest((Widget)shell, &replygeom, NULL); /* fall through. */ case XtGeometryYes: DoLayout(wid); break; case XtGeometryNo: case XtGeometryDone: break; } } /* * Resize() */ static void Resize(Widget w) { DoLayout(w); } /* * When using an override redirect window, it is safe to draw to the * window as soon as you have mapped it; you need not wait for exposure * events to arrive. So ... to force shells to post quickly, we will * redraw all of the items now, and ignore the exposure events we receive * later. */ static void _XmFastExpose(Widget widg) { register int i; register Widget child; XmGrabShellWidget gs = (XmGrabShellWidget)widg; _XmProcessLock(); (*(XtClass(widg)->core_class.expose)) (widg, NULL, NULL); _XmProcessUnlock(); /* Process each windowed child */ for (i = 0; i < gs->composite.num_children; i++) { child = gs->composite.children[i]; if (XtIsWidget(child) && XtIsManaged(child)) { _XmProcessLock(); (*(XtClass(child)->core_class.expose)) (child, NULL, NULL); _XmProcessUnlock(); } } XFlush(XtDisplay(widg)); DrawBorder(widg); } /* * DrawBorder() */ static void DrawBorder(Widget widg) { XmGrabShellWidget gs = (XmGrabShellWidget)widg; int offset = 0; XmeDrawShadows(XtDisplay(widg), XtWindow(widg), gs->grab_shell.top_shadow_GC, gs->grab_shell.bottom_shadow_GC, offset, offset, XtWidth(widg) - 2 * offset, XtHeight(widg) - 2 * offset, gs->grab_shell.shadow_thickness, XmSHADOW_OUT); } /* * IgnoreXErrors() * An XErrorHandler that smothers errors. */ /*ARGSUSED*/ static int IgnoreXErrors(Display *dpy, /* unused */ XErrorEvent *event) /* unused */ { return 0; } /******************* * Public Routines * *******************/ Widget XmCreateGrabShell(Widget parent, char *name, ArgList al, Cardinal ac) { return XtCreatePopupShell(name, xmGrabShellWidgetClass, parent, al, ac); }