From: cap@rod.mitre.org (Craig Prall)
Subject: Re: Keep Icon or Window Always On Top?
Organization: The MITRE Corporation
Date: Mon, 16 Nov 1992 17:35:45 GMT

In article 17619, Richard Baughman writes:

> Is there a way to specify to X/Motif to *never* cover up an icon or,
> alternatively, a window?  We are writing an application in OSF/Motif that
> needs an alarm window containing important messages that should never be
> covered up.

> If this is not feasible, has anyone implemented something like an alarm system
> with pseudo-realtime events happening that must be brought to an operator`s
> attention without preventing him/her from attending to the underlying problems?

This is one of those FAQs that I have found a couple of answers for. I
will start by saying this may not apply to icons. They seem to be a 
different animal and are not held to the rules that normal windows are
required to follow.

There are a couple of ways to keep your window on top. The problem with
doing this is that it is anti-social in general. The typical way to 
bring up alarms would be to use a popup dialog. This can even force the
user to dismiss the window before doing anything else, but generally it
just puts up a window and lets the user go on. I am assuming that
you looked into that and decided that wasn't for you. 

As an alternative, you could use XRaiseWindow(display, window_id) every 
time that you add new info to the alarm window. Accompanied with XBeep, 
that should work for you.

If you don't ever want the window to be covered up even when there is no
recently new data, use something like the following:

--------- Beginning of code fragment ----------
#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>

void      check_visible();
static void      x_error();
static void      xt_error();

char *pgmname;

main (argc, argv)
     int                  argc;
     char                 *argv [ ];
{
  Widget               toplevel,  rc_widget, msg_widget;
  XtAppContext	       app;
  Arg                  wargs [20];
  int                  i, n, found;
  char		       geoBuf[80];	

  pgmname = argv[0];

  /* Initialize the Intrinsics */
  toplevel = XtVaAppInitialize(&app, "Banner1", NULL, 0, 
			  &argc, argv, NULL, NULL);

  /* Set the banner resources */
  n=0;
  sprintf(geoBuf,"%sx%s+0+0",BWIDTH,BHEIGHT);
  XtSetArg(wargs[n], XmNgeometry, geoBuf); n++;
  XtSetArg(wargs[n], XmNmwmDecorations, MWM_DECOR_BORDER); n++;
  XtSetValues(toplevel, wargs, n);
           ...	[ Lots of code deleted here. ]
           ...
  /* Register the error handlers to be called if Xlib or Xt errors occur */
  XtAppSetErrorHandler(app, xt_error);
  XtAppSetWarningHandler(app, xt_error);
  XSetErrorHandler(x_error);

  /* Register the event handler to be called when the visibility
     of the window changes */
  XtAddEventHandler(toplevel, VisibilityChangeMask, FALSE,
		    check_visible, NULL);
  
  /* Realize the widgets and enter the event loop */
  XtRealizeWidget(toplevel);
  XtAppMainLoop(app);
}
    
void check_visible(w, client_data, visibility)
     Widget w;
     caddr_t client_data;
     XVisibilityEvent *visibility;
{
  Window            thisWin;
  Display	   *dpy;

  dpy = XtDisplay(w);
  thisWin = XtWindow(w);
  XRaiseWindow(dpy,thisWin);
}


static void x_error(dpy, err_event)
Display		*dpy;
XErrorEvent	*err_event;
{
   char		buf[80];

   XGetErrorText(dpy, err_event->error_code, buf, sizeof buf);

   fprintf(stderr,"security banner: Xlib Error:\n");
   fprintf(stderr,"security banner: <%s>\n",buf);

}


static void xt_error(message)
char		*message;
{

   fprintf(stderr,"security banner: Xt Error:\n");
   fprintf(stderr,"security banner: <%s>\n",message);

}
--------- End of code fragment ----------

The above code fragment is part of a security classification banner 
program that I wrote. The security classification is at the top of 
the screen and must not be covered up. That is, assuming we ever want 
the system accredited for classified work. You won't need all of the 
#includes, but I am not sure which ones you do need. This will put your 
alarm window on top of any window that has the gall to cover it up.

I wasn't willing to stop there. This kept the banner on top, but it had
the nasty property that it covered up the title bars of the windows that
overlapped with it, making it hard to resize or move. (The users had no
problem grabbing the title bar to move windows since that is just like the 
Macs and Window 3.x applications they had been using. However, they didn't 
like using button 2 and grabbing the border to move the window. You have 
to be too accurate of a "mouser".) What I wanted was for the banner to
try to move the other windows off of it by performing a XMoveWindow request
to move them straight down. (You could change this anyway you want to
move it left, right, or up.)

--------- Beginning of code fragment ----------
#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include <Xm/Xm.h>
#include <Xm/MwmUtil.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>

#define BWIDTH		"1144"      
#define BHEIGHT		"60"      
#define EXTRA_FOR_BORDERS 6
#define EXTRA_FOR_TITLE_BAR 1

void      check_visible();
Window	  XmuClientWindow();
XmString  xs_concat_words();
static void      x_error();
static void      xt_error();

char *pgmname;

main (argc, argv)
     int                  argc;
     char                 *argv [ ];
{
  Widget               toplevel,  rc_widget, msg_widget;
  XtAppContext	       app;
  Arg                  wargs [20];
  int                  i, n, found;
  XmString             message;
  XFontStruct          *fontstruct;
  char		       geoBuf[80];	
  int		       dummyargc;

  	...
  
  pgmname = argv[0];

  /* Initialize the Intrinsics */
  toplevel = XtVaAppInitialize(&app, "Banner1", NULL, 0, 
			  &argc, argv, NULL, NULL);

  /* Set the banner resources - border only */
  n=0;
  sprintf(geoBuf,"%sx%s+0+0",BWIDTH,BHEIGHT);
  XtSetArg(wargs[n], XmNgeometry, geoBuf); n++;
  XtSetArg(wargs[n], XmNmwmDecorations, MWM_DECOR_BORDER); n++;
  XtSetValues(toplevel, wargs, n);
	...
  
  /* Register the error handlers to be called if Xlib or Xt errors occur */
  XtAppSetErrorHandler(app, xt_error);
  XtAppSetWarningHandler(app, xt_error);
  XSetErrorHandler(x_error);

  /* Register the event handler to be called when the visibility
     of the window changes */
  XtAddEventHandler(toplevel, VisibilityChangeMask, FALSE,
		    check_visible, NULL);
  
  /* Realize the widgets and enter the event loop */
  XtRealizeWidget(toplevel);
  XtAppMainLoop(app);
}
    
void check_visible(w, client_data, visibility)
     Widget w;
     caddr_t client_data;
     XVisibilityEvent *visibility;
{
  Window            rootWin, rootWinRet, parent, thisWin, otherWin,
                    clientWindow;
  Window           *children;
  unsigned int      num_children, i;
  Status            success;
  Display	   *dpy;
  XWindowAttributes xwa;


  /* The following "if" should help cut down on the amount of processing.
     However, if a window is placed so that its top (the window inside
     the manager, not the manager's frame window) is just at the bottom
     of the banner, then visibility->state is VisibilityUnobscured even
     though the window manager's title bar (of the frame window) is
     clearly obscuring the banner. This is likely a bug in X. The 
     solution is to perform all processing whenever called. */
  /* if  (visibility->state  != VisibilityUnobscured)  { */
    
    /* This security banner has been obscured -- remedy that */
    
    /* Find all the children of the root window */
    dpy = XtDisplay(w);
    thisWin = XtWindow(w);
    rootWin = DefaultRootWindow(dpy);

    success = XQueryTree(dpy, rootWin, &rootWinRet, &parent, &children,
			 &num_children);

    if (success) {

       /* For each child of the root window that is a top level
	  shell, see if its topmost y pixel location is above
	  the bottom pixel location of the banner. If it is
	  make a geometry request to move the window down. */
       for (i=0; i < num_children; i++) {
	 otherWin = children[i];

	 if (otherWin != thisWin) {
	   clientWindow = XmuClientWindow(dpy,otherWin);

	   /* Only true client windows will come back with a
	      value other than otherWin (the original) */

	   if (clientWindow && clientWindow != otherWin &&
	       clientWindow != thisWin) {

	     /* We have to get the attributes from the otherWin
		not the clientWindow -- the client window has
		no x or y attributes */
	     if (!XGetWindowAttributes(dpy, otherWin, &xwa)) {
	       fprintf(stderr, "%s: Unable to get attributes for window\n",
		       pgmname);
	     }  

	     /* Does the other window overlap the banner? */
	     if (xwa.y < atoi(BHEIGHT)) {
	       /* The other window DOES overlap the security banner */
	       /* Move it down */
	       /* The following if is also due to the Visibility Obscured
		  problem. For those windows that are overlapping the 
		  banner, but visibility->state is VisibilityUnobscured,
		  the XMoveWindow coordinates seem messed up somehow too.
		  They are off by the width of the title bar. So add a 
		  little extra in that case.
	       */
	       if (visibility->state == VisibilityUnobscured)  {
	         XMoveWindow(dpy, clientWindow, xwa.x, 
		    	     atoi(BHEIGHT)+EXTRA_FOR_BORDERS+
                             EXTRA_FOR_TITLE_BAR);
               }
	       else {
	         XMoveWindow(dpy, clientWindow, xwa.x, 
			     atoi(BHEIGHT)+EXTRA_FOR_BORDERS);
               }
	     }
	   }
	 }
       }
       XFree(children);
     }
    
    /* The following is a 'hack' to handle the case where an application 
       draws more windows on the banner while this callback is handling 
       the Visibility Notify event, which is generated only once for the 
       first window that obscured the banner. This forces the banner to 
       the front, then to the back where any "new" windows drawn on the 
       banner cause new Visibility Notify events." */
    XRaiseWindow(dpy,thisWin);
    XLowerWindow(dpy,thisWin);
  /* } */ /* See note on if stmt above */
}

/* The following routines are straight out of MIT's sample code. */

 /* $XConsortium: ClientWin.c,v 1.3 89/12/10 10:26:35 rws Exp $ */

 /* 
  * Copyright 1989 by the Massachusetts Institute of Technology
  *
  * Permission to use, copy, modify, and distribute this software and its
  * documentation for any purpose and without fee is hereby granted, provided 
  * that the above copyright notice appear in all copies and that both that 
  * copyright notice and this permission notice appear in supporting 
  * documentation, and that the name of M.I.T. not be used in advertising
  * or publicity pertaining to distribution of the software without specific, 
  * written prior permission. M.I.T. makes no representations about the 
  * suitability of this software for any purpose.  It is provided "as is"
  * without express or implied warranty.
  *
  */

#include <X11/Xlib.h>
#include <X11/Xatom.h>

static Window TryChildren();

/* Find a window with WM_STATE, else return win itself, as per ICCCM */

Window XmuClientWindow (dpy, win)
     Display *dpy;
     Window win;
{
  Atom WM_STATE;
  Atom type = None;
  int format;
  unsigned long nitems, after;
  unsigned char *data;
  Window inf;
  
  WM_STATE = XInternAtom(dpy, "WM_STATE", True);
  if (!WM_STATE)
    return win;
  XGetWindowProperty(dpy, win, WM_STATE, 0, 0, False, AnyPropertyType,
		     &type, &format, &nitems, &after, &data);
  if (type)
    return win;
  inf = TryChildren(dpy, win, WM_STATE);
  if (!inf)
    inf = win;
  return inf;
}

static
  Window TryChildren (dpy, win, WM_STATE)
Display *dpy;
Window win;
Atom WM_STATE;
{
  Window root, parent;
  Window *children;
  unsigned int nchildren;
  unsigned int i;
  Atom type = None;
  int format;
  unsigned long nitems, after;
  unsigned char *data;
  Window inf = 0;
  
  if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren))
    return 0;
  for (i = 0; !inf && (i < nchildren); i++) {
    XGetWindowProperty(dpy, children[i], WM_STATE, 0, 0, False,
		       AnyPropertyType, &type, &format, &nitems,
		       &after, &data);
    if (type)
      inf = children[i];
  }
  for (i = 0; !inf && (i < nchildren); i++)
    inf = TryChildren(dpy, children[i], WM_STATE);
  if (children) XFree((char *)children);
  return inf;
}


static void x_error(dpy, err_event)
Display		*dpy;
XErrorEvent	*err_event;
{
   char		buf[80];

   XGetErrorText(dpy, err_event->error_code, buf, sizeof buf);

   fprintf(stderr,"security banner: Xlib Error:\n");
   fprintf(stderr,"security banner: <%s>\n",buf);

}


static void xt_error(message)
char		*message;
{

   fprintf(stderr,"security banner: Xt Error:\n");
   fprintf(stderr,"security banner: <%s>\n",message);

}
--------- End of code fragment ----------

This seems to be working well for me. (Actually it looks quite neat.)
I have found out that if you are using the OpenWindows (2.0 or 3.0) 
(XNeWS server) with the Motif window manager, that the #define for 
EXTRA_FOR_BORDERS needs to be around 35 (or so) or you get the effect 
that the windows that cover the banner get "marched off the screen." 

This took me a while to get it right, so I thought I would try to 
keep others from going through the pain. Hope it helps.

Craig Prall
The MITRE Corporation, Workstation System Engineering Center
internet: cap@mitre.org


Newsgroups: comp.windows.x.motif,comp.windows.x