Since i recently ran into the same issue:
You can do it by bypassing Qt. For the windows part see @JakePetroules answer.
XCB (X11) version i use:
#ifdef Q_OS_LINUX
#include <QX11Info>
#include <xcb/xcb.h>
// Just a simple atom cache helper
xcb_atom_t xcb_get_atom(const char *name){
if (!QX11Info::isPlatformX11()){
return XCB_ATOM_NONE;
}
auto key = QString(name);
if(_xcb_atom_cache.contains(key)){
return _xcb_atom_cache[key];
}
xcb_connection_t *connection = QX11Info::connection();
xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
if(!reply){
return XCB_ATOM_NONE;
}
xcb_atom_t atom = reply->atom;
if(atom == XCB_ATOM_NONE){
DEBUG("Unknown Atom response from XServer: " << name);
} else {
_xcb_atom_cache.insert(key, atom);
}
free(reply);
return atom;
}
void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
{
auto connection = QX11Info::connection();
xcb_atom_t type_atom = xcb_get_atom(type);
xcb_atom_t prop_atom = xcb_get_atom(prop);
xcb_client_message_event_t event;
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.sequence = 0;
event.window = window;
event.type = type_atom;
event.data.data32[0] = set ? 1 : 0;
event.data.data32[1] = prop_atom;
event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
event.data.data32[3] = 0;
event.data.data32[4] = 0;
xcb_send_event(connection, 0, window,
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE,
(const char *)&event);
xcb_flush(connection);
}
#endif
Use like:
xcb_update_prop(true, window()->winId(), "_NET_WM_STATE", "_NET_WM_STATE_ABOVE", "_NET_WM_STATE_STAYS_ON_TOP");
Its a bit hackish, but it worked fine on Mate, KDE, GNOME3, XFCE and openbox.