setUsingNativeTitleBar(true)


#1

This method is giving me some headaches.
It works perfect on other operating systems (win and mac), but on linux I’m experiencing some troubles.
I’m using a Ubuntu 10.04 distribution.
When I add to the Desktop a child DocumentWindow and call setUsingNativeTitleBar(true) in it,
the contentComponent of the new window is displayed on the desktop, but there are no frames nor titlebar around it.
So I can’t even move or close the window.
The main window of the program is not affected by this, though.
Anyone else experiencing the same issue?

…and yes, I’m using the tip.


#2

you say it’s a child DocumentWindow… Why not just remove it from its parent before putting it on the desktop?


#3

Well, it’s not a child component (I think).
I said child meaning that it’s not the main window of the program.
The new window’s constructor is like this:

ExampleWindow::ExampleWindow()
: DocumentWindow ("ExampleWindow",
				  Colours::lightgrey,
				  DocumentWindow::allButtons, true) //I'm adding it to the desktop
{
    setUsingNativeTitleBar(true);
}

And in the main window I’m writing

ExampleWindow* const newWindow = new ExampleWindow();
newWindow->setVisible(true);

and not

ExampleWindow* const newWindow = new ExampleWindow();
addAndMakeVisible(newWindow); //this should make the newWindow a child, right?

#4

Ok, so it’s not a child. Can you reproduce this in the juce demo?


#5

You can use the example I attached in this post
http://www.rawmaterialsoftware.com/viewtopic.php?f=2&t=5460

And add a “true” in the FlaggedWindow constructor.


#6

Your example code’s not exactly robust - you not only mess about with the windows flags, but you do it by subtracting (!!!) the flags you want to turn off… You might want to go and have a think about why subtraction might not be the best operation for clearing bit flags!

If you really do think this is a bug, take the juce demo and add the minimum possible code to reproduce it, and I’ll look at that.


#7

Ok, sorry! (sharpen knives piercing my stomach)
I’ll do it.


#8

I found the problem.
The issue occours If I set the new window to be always on top.
I can’t upload here the whole juce demo (with the projects), so I attached only the source code.


#9

So does it happen if you just add “setAlwaysOnTop (true)” to the main window in the demo?


#10

Can’t try right now, as soon as I get home I’ll do it.


#11

I checked.
Yes, it happens also to the MainWindow.
Just call

setUsingNativeTitleBar(true);
setAlwaysOnTop(true);

And doh! The window disappears, leaving the contentComponent on the screen only.


#12

Ok, thanks. Probably a window manager weirdness, I’ll take a look.


#13

Seems that because I’ve used the X override_redirect flag to keep the window on top, window managers don’t add their decoration to windows that have that flag set… Bit annoying, and I can’t see any obvious way around this. Any X experts know any more about this?


#14

XRaiseWindow ?
Please check this: http://www.ist.co.uk/xd/support/faqs.html#HDR50


#15

Hmm. Quite nasty (though the way it’s currently done is also a pretty nasty). The main problem I can see with doing it that way is that if you had two always-on-top windows, they’d each keep trying to pop themselves to the top in a feedback loop, which would take some cunning coding to work around…


#16

Not too hard actually (if you want to mimic Windows behaviour), the pseudo-code would be something like:

    void raiseWindow(other, w)
    {   
        if (other != w && XtGetEventHandler(other) != keep_on_top) 
            XRaiseWindow(XtDisplay(w), XtWindow(w)); 
    }
 
    XtAddEventHandler(sh1, VisibilityChangeMask, False, keep_on_top, NULL);
    void keep_on_top (w, client_data, event) 
    {
	    if ((event->xvisibility.state == VisibilityFullyObscured) ||
		    (event->xvisibility.state == VisibilityPartiallyObscured)) 
	    { 
                topLeftWindow = getWindowAtPos(topLeftCorner);
                topRightWindow = getWindowAtPos(topRightCorner);
                bottomLeftWindow = getWindowAtPos(bottomLeftCorner);
                bottomRightWindow = getWindowAtPos(bottomRightCorner);

                raiseWindow(topLeftWindow, w);
                raiseWindow(topRightWindow, w);
                raiseWindow(bottomLeftWindow, w);
                raiseWindow(bottomRightWindow, w);
	   }
	}

#17

Here’s the code of xosd:

/* Tell window manager to put window topmost. {{{ */
void
stay_on_top(Display * dpy, Window win)
{
  Atom gnome, net_wm, type;
  int format;
  unsigned long nitems, bytesafter;
  unsigned char *args = NULL;
  Window root = DefaultRootWindow(dpy);

  /*
   * build atoms 
   */
  gnome = XInternAtom(dpy, "_WIN_SUPPORTING_WM_CHECK", False);
  net_wm = XInternAtom(dpy, "_NET_SUPPORTED", False);

  /*
   * gnome-compilant 
   * tested with icewm + WindowMaker 
   */
  if (Success == XGetWindowProperty
      (dpy, root, gnome, 0, (65536 / sizeof(long)), False,
       AnyPropertyType, &type, &format, &nitems, &bytesafter, &args) &&
      nitems > 0) {
    /*
     * FIXME: check capabilities 
     */
    XClientMessageEvent xev;
    Atom gnome_layer = XInternAtom(dpy, "_WIN_LAYER", False);

    memset(&xev, 0, sizeof(xev));
    xev.type = ClientMessage;
    xev.window = win;
    xev.message_type = gnome_layer;
    xev.format = 32;
    xev.data.l[0] = 6 /* WIN_LAYER_ONTOP */ ;

    XSendEvent(dpy, DefaultRootWindow(dpy), False, SubstructureNotifyMask,
               (XEvent *) & xev);
    XFree(args);
  }
  /*
   * netwm compliant.
   * tested with kde 
   */
  else if (Success == XGetWindowProperty
           (dpy, root, net_wm, 0, (65536 / sizeof(long)), False,
            AnyPropertyType, &type, &format, &nitems, &bytesafter, &args)
           && nitems > 0) {
    XEvent e;
    Atom net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
    Atom net_wm_top = XInternAtom(dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);

    memset(&e, 0, sizeof(e));
    e.xclient.type = ClientMessage;
    e.xclient.message_type = net_wm_state;
    e.xclient.display = dpy;
    e.xclient.window = win;
    e.xclient.format = 32;
    e.xclient.data.l[0] = 1 /* _NET_WM_STATE_ADD */ ;
    e.xclient.data.l[1] = net_wm_top;
    e.xclient.data.l[2] = 0l;
    e.xclient.data.l[3] = 0l;
    e.xclient.data.l[4] = 0l;

    XSendEvent(dpy, DefaultRootWindow(dpy), False,
               SubstructureRedirectMask, &e);
    XFree(args);
  }
  XRaiseWindow(dpy, win);
}

Which use window manager instead of the “struggle for life” approach


#18

Thanks - that looks like a more promising approach!


#19

Well, I tried that code in KDE and it doesn’t seem to do anything at all…?


#20

I don’t really know. XOSD is an old demonstration code, maybe it doesn’t work anymore with KDE or gnome.
The basic idea of XOSD is to tell the windows manager to move the window in the “Always-On-Top” layer.

The atom hint that comes back often in code is XA_NET_WM_STATE_ABOVE or _NET_WM_STATE_ABOVE and XA_NET_WM_STATE_STAYS_ON_TOP

For example, here’s the mplayer implementation (should be more recent, but the code is harder to understand):

static int vo_wm_detect(void)
{
    int i;
    int wm = 0;
    unsigned long nitems;
    Atom *args = NULL;

    if (WinID >= 0)
        return 0;

// -- supports layers
    if (x11_get_property(XA_WIN_PROTOCOLS, &args, &nitems))
    {
        mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports layers.\n");
        for (i = 0; i < nitems; i++)
        {
            if (args[i] == XA_WIN_LAYER)
            {
                wm |= vo_wm_LAYER;
                metacity_hack |= 1;
            } else
                // metacity is the only manager I know which reports support only for _WIN_LAYER
                // hint in _WIN_PROTOCOLS (what's more support for it is broken)
                metacity_hack |= 2;
        }
        XFree(args);
        if (wm && (metacity_hack == 1))
        {
            // metacity reports that it supports layers, but it is not really truth :-)
            wm ^= vo_wm_LAYER;
            mp_msg(MSGT_VO, MSGL_V,
                   "[x11] Using workaround for Metacity bugs.\n");
        }
    }
// --- netwm
    if (x11_get_property(XA_NET_SUPPORTED, &args, &nitems))
    {
        mp_msg(MSGT_VO, MSGL_V, "[x11] Detected wm supports NetWM.\n");
        for (i = 0; i < nitems; i++)
            wm |= net_wm_support_state_test(args[i]);
        XFree(args);
#if 0
        // ugly hack for broken OpenBox _NET_WM_STATE_FULLSCREEN support
        // (in their implementation it only changes internal state of window, nothing more!!!)
        if (wm & vo_wm_FULLSCREEN)
        {
            if (x11_get_property(XA_BLACKBOX_PID, &args, &nitems))
            {
                mp_msg(MSGT_VO, MSGL_V,
                       "[x11] Detected wm is a broken OpenBox.\n");
                wm ^= vo_wm_FULLSCREEN;
            }
            XFree(args);
        }
#endif
    }

    if (wm == 0)
        mp_msg(MSGT_VO, MSGL_V, "[x11] Unknown wm type...\n");
    return wm;
}

static void init_atoms(void)
{
    XA_INIT(_NET_SUPPORTED);
    XA_INIT(_NET_WM_STATE);
    XA_INIT(_NET_WM_STATE_FULLSCREEN);
    XA_INIT(_NET_WM_STATE_ABOVE);
    XA_INIT(_NET_WM_STATE_STAYS_ON_TOP);
    XA_INIT(_NET_WM_STATE_BELOW);
    XA_INIT(_NET_WM_PID);
    XA_INIT(_WIN_PROTOCOLS);
    XA_INIT(_WIN_LAYER);
    XA_INIT(_WIN_HINTS);
    XA_INIT(_BLACKBOX_PID);
}

static int vo_x11_get_gnome_layer(Display * mDisplay, Window win)
{
    Atom type;
    int format;
    unsigned long nitems;
    unsigned long bytesafter;
    unsigned short *args = NULL;

    if (XGetWindowProperty(mDisplay, win, XA_WIN_LAYER, 0, 16384,
                           False, AnyPropertyType, &type, &format, &nitems,
                           &bytesafter,
                           (unsigned char **) &args) == Success
        && nitems > 0 && args)
    {
        mp_msg(MSGT_VO, MSGL_V, "[x11] original window layer is %d.\n",
               *args);
        return *args;
    }
    return WIN_LAYER_NORMAL;
}

void vo_x11_setlayer(Display * mDisplay, Window vo_window, int layer)
{
    if (WinID >= 0)
        return;

    if (vo_fs_type & vo_wm_LAYER)
    {
        XClientMessageEvent xev;

        if (!orig_layer)
            orig_layer = vo_x11_get_gnome_layer(mDisplay, vo_window);

        memset(&xev, 0, sizeof(xev));
        xev.type = ClientMessage;
        xev.display = mDisplay;
        xev.window = vo_window;
        xev.message_type = XA_WIN_LAYER;
        xev.format = 32;
        xev.data.l[0] = layer ? fs_layer : orig_layer;  // if not fullscreen, stay on default layer
        xev.data.l[1] = CurrentTime;
        mp_msg(MSGT_VO, MSGL_V,
               "[x11] Layered style stay on top (layer %d).\n",
               xev.data.l[0]);
        XSendEvent(mDisplay, mRootWin, False, SubstructureNotifyMask,
                   (XEvent *) & xev);
    } else if (vo_fs_type & vo_wm_NETWM)
    {
        XClientMessageEvent xev;
        char *state;

        memset(&xev, 0, sizeof(xev));
        xev.type = ClientMessage;
        xev.message_type = XA_NET_WM_STATE;
        xev.display = mDisplay;
        xev.window = vo_window;
        xev.format = 32;
        xev.data.l[0] = layer;

        if (vo_fs_type & vo_wm_STAYS_ON_TOP)
            xev.data.l[1] = XA_NET_WM_STATE_STAYS_ON_TOP;
        else if (vo_fs_type & vo_wm_ABOVE)
            xev.data.l[1] = XA_NET_WM_STATE_ABOVE;
        else if (vo_fs_type & vo_wm_FULLSCREEN)
            xev.data.l[1] = XA_NET_WM_STATE_FULLSCREEN;
        else if (vo_fs_type & vo_wm_BELOW)
            // This is not fallback. We can safely assume that situation where
            // only NETWM_STATE_BELOW is supported and others not, doesn't exist.
            xev.data.l[1] = XA_NET_WM_STATE_BELOW;

        XSendEvent(mDisplay, mRootWin, False, SubstructureRedirectMask,
                   (XEvent *) & xev);
        state = XGetAtomName(mDisplay, xev.data.l[1]);
        mp_msg(MSGT_VO, MSGL_V,
               "[x11] NET style stay on top (layer %d). Using state %s.\n",
               layer, state);
        XFree(state);
    }
}

From there: http://www.google.fr/codesearch/p?hl=fr#RGCD84x9Jg0/trunk/mplayer/libvo/x11_common.c&q=WM_STATE_ABOVE&sa=N&cd=5&ct=rc