Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

X11/Xlib: Window always on top

A window should stay on top of all other windows. Is this somehow possible with plain x11/xlib? Googling for "Always on top" and "x11" / "xlib" didn't return anything useful.

I'd avoid toolkits like GTK+, if somehow possible.

I'm using Ubuntu with gnome desktop. In the window menu, there's an option "Always On Top". Is this provided by the X server or the window manager? If the second is the case, is there a general function that can be called for nearly any wm? Or how to do this in an "X11-generic" way?


Edit: I implemented fizzer's answer, now having following code:
XSelectInput(this->display, this->window,
    ButtonPressMask |
    StructureNotifyMask |
    ExposureMask |
    KeyPressMask |
    PropertyChangeMask |
    VisibilityChangeMask ); 
// ...
// In a loop:
if (XPending(this->display) >= 0)
{
    XNextEvent(this->display, &ev);
    switch(ev.type) {
    // ...
    case VisibilityNotify:
        XRaiseWindow(this->display, this->window);
        XFlush(this->display);
    break;
    // ...
    }
}

But the eventhandling and raising nearly never gets executed even my mask is correct?!

like image 767
Atmocreations Avatar asked Dec 03 '10 11:12

Atmocreations


3 Answers

#define _NET_WM_STATE_REMOVE        0    // remove/unset property
#define _NET_WM_STATE_ADD           1    // add/set property
#define _NET_WM_STATE_TOGGLE        2    // toggle property

Bool MakeAlwaysOnTop(Display* display, Window root, Window mywin)
{
    Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
    if( wmStateAbove != None ) {
        printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
        return False;
    }
    
    Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
    if( wmNetWmState != None ) {
        printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
        return False;
    }

    // set window always on top hint
    if( wmStateAbove != None )
    {
        XClientMessageEvent xclient;
        memset( &xclient, 0, sizeof (xclient) );
        //
        //window  = the respective client window
        //message_type = _NET_WM_STATE
        //format = 32
        //data.l[0] = the action, as listed below
        //data.l[1] = first property to alter
        //data.l[2] = second property to alter
        //data.l[3] = source indication (0-unk,1-normal app,2-pager)
        //other data.l[] elements = 0
        //
        xclient.type = ClientMessage;
        xclient.window = mywin;              // GDK_WINDOW_XID(window);
        xclient.message_type = wmNetWmState; //gdk_x11_get_xatom_by_name_for_display( display, "_NET_WM_STATE" );
        xclient.format = 32;
        xclient.data.l[0] = _NET_WM_STATE_ADD; // add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
        xclient.data.l[1] = wmStateAbove;      //gdk_x11_atom_to_xatom_for_display (display, state1);
        xclient.data.l[2] = 0;                 //gdk_x11_atom_to_xatom_for_display (display, state2);
        xclient.data.l[3] = 0;
        xclient.data.l[4] = 0;
        //gdk_wmspec_change_state( FALSE, window,
        //  gdk_atom_intern_static_string ("_NET_WM_STATE_BELOW"),
        //  GDK_NONE );
        XSendEvent( display,
          //mywin - wrong, not app window, send to root window!
          root, // <-- DefaultRootWindow( display )
          False,
          SubstructureRedirectMask | SubstructureNotifyMask,
          (XEvent *)&xclient );

        XFlush(display);

        return True;
    }

    return False;
}
like image 152
user2323699 Avatar answered Dec 29 '22 19:12

user2323699


You don't want to use XRaiseWindow() to try to stay on top. Some window managers will ignore it entirely. For those that don't, consider what happens if more than one app tries to do this. Boom! That's why the window manager is in charge of stacking windows, not the app.

The way you do this is to use the protocols defined in the Extended Window Manager Hints (EWMH), see: http://www.freedesktop.org/wiki/Specifications/wm-spec

Specifically here you want _NET_WM_STATE_ABOVE which is how the "Always on Top" menu item works.

If you aren't using a toolkit you'll want to get used to scavenging in toolkit source code to figure out how to do things. In this case you could look at the function gdk_window_set_keep_above() in GTK+'s X11 backend. That will show how to use the _NET_WM_STATE_ABOVE hint.

like image 31
Havoc P Avatar answered Dec 29 '22 20:12

Havoc P


I wrote something like this in Xlib many years ago. It's a few lines of code. When your window is partially obscured you get a VisibilityNotify event, then call XRaiseWindow. Watch out for the case where two of your 'always on top' windows overlap.

like image 24
fizzer Avatar answered Dec 29 '22 19:12

fizzer