6

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?!

unwind
  • 391,730
  • 64
  • 469
  • 606
Atmocreations
  • 9,923
  • 15
  • 67
  • 102
  • Virtually all low-level windowing toolkits are defined in either or both of these languages, so if it can be done, then it can be done in C/C++. This isn't really a language question. – Marcelo Cantos Dec 03 '10 at 11:58
  • changed the question. sure it's not about whether it was possible using c/c++ but if it was possible using the minimal binding of **x11/xlib** only – Atmocreations Dec 03 '10 at 12:05
  • Do you get the other event types? – fizzer Dec 03 '10 at 13:26
  • Also, is your window at the top level - i.e. a child of the root window? You can only raise a window relative to siblings. – fizzer Dec 03 '10 at 13:38
  • Yes, I do get the other events and the win is child of the root win. Is it because I do the following? XserverRegion region = XFixesCreateRegion( this->display, 0, 0 ); XFixesSetWindowShapeRegion(this->display, this->window, ShapeInput, 0, 0, region ); – Atmocreations Dec 03 '10 at 13:53
  • No idea. Not done X for ~ 12 years, before XFixes – fizzer Dec 03 '10 at 14:00
  • Oh - it's not an InputOnly window is it? – fizzer Dec 03 '10 at 14:02

4 Answers4

15
#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;
}
KlemenPl
  • 354
  • 4
  • 21
user2323699
  • 151
  • 1
  • 4
  • 1
    A superb solution thank you! I like the comparison to the GDK in the comments! I am curious though how come you did not set `xclient.send_event`? – yatg Jul 21 '15 at 01:30
  • 1
    `XFlush` must be called after `XSendEvent`, otherwise it doesnt set the window to be always on top. – yatg Jul 23 '15 at 21:51
  • 1
    Late comment, but XSendEvent will set xclient.send_event for you. – Joe Sewell Jul 13 '20 at 17:47
7

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.

Havoc P
  • 8,365
  • 1
  • 31
  • 46
  • Thanks. Sure you're right about not using XRaiseWindow(). Was my thought, too. But I already took a look at gdk_window_set_keep_above() before and I've tried to send an XEvent "_NET_WM_STATE_ABOVE" to the specific window. Which didn't do anything at all... Using Compiz and Metacity. – Atmocreations Dec 03 '10 at 21:48
  • there are mistakes you can make here, such as getting the details of the XSendEvent wrong, or using an event on a not-yet-mapped window when you should instead set the property. also the WM may ignore the message for some semantic window types. anyway if you post a single-file compilable test program people could try to diagnose. – Havoc P Dec 03 '10 at 21:50
  • Hi Havoc! I was wondering if you could link me to GDK backend sourcecode so i can see how they do the `gdk_window_set_keep_above`. Also if you could link me to GTK sourcecode i wanted to look into `gtk_window_set_above` as well: https://developer.gnome.org/gtk3/unstable/GtkWindow.html#gtk-window-set-keep-above – yatg Jul 21 '15 at 16:51
2

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.

fizzer
  • 13,551
  • 9
  • 39
  • 61
  • 3
    This is not a good idea because anytime you have two apps doing this they will "fight" for the top. There's a dedicated hint instead in the EWMH. – Havoc P Dec 03 '10 at 16:16
  • In the rare case where you don't have a window manager, this technique is pretty much all you've got, but in that case you are likely very in control of what's running, so avoiding having two windows fight for the top is much simpler. – Michael Kohne Mar 09 '18 at 20:05
-8

Use Actual Title Buttons (http://www.actualtools.com/titlebuttons/) for example. It allows to stay any windows always on top , roll up, make transparency and etc..

figaro
  • 1