Question

I have created a frameless Qt/QML window, and I really would like to know any programmatic means of setting its "Always On Top" system menu flag. Clicking ALT+SPACE I can bring up the system menu for the frameless window, and by clicking the "Always On Top" option the window does keep always on top, but I've not found a programmatic way of doing the same. The Qt.WindowStaysOnTopHint doesn't work, and trying wmctrl -r "window name" -b add,above doesn't work either, even though wmctrl does work for other windows. wmctrl not working for my window of interest apparently has something to do with the N/A for the machine name column on wmctrl -l:

francisco@Ubuntu:~$ wmctrl -l
0x02600006  0 Ubuntu Área de trabalho
0x03c00002  0 Ubuntu XdndCollectionWindowImp
0x03c00005  0 Ubuntu unity-launcher
0x03c00008  0 Ubuntu unity-panel
0x03c0000b  0 Ubuntu unity-dash
0x03c0000c  0 Ubuntu Hud
0x046000b3  0 Ubuntu How to make a window aways on top? - Stack Overflow - Mozilla Firefox
0x0520000b  0    N/A Qt Creator
0x05002396  0 Ubuntu francisco@Ubuntu: ~
0x0540000b  0    N/A backlight

I've also gone through this procedure but as for the user asking, it's not working for me either, same behavior. The _NET_WM_STATE_ABOVE is set, but focusing the window and then checking the flag again it's not there anymore, it's sticky only when clicking through system menu.

This is the QML: https://gist.github.com/oblitum/8050586

Related askubuntu question: https://askubuntu.com/questions/394998

EDIT

Notice

In the related askubuntu question, it was found that there should be a bug on wmctrl for targeting certain windows through their names. Using wmctrl -i -r <window id> -b add,above also solves the issue.

Était-ce utile?

La solution

the EWMH specification explicitly states that:

_NET_WM_STATE_ABOVE and _NET_WM_STATE_BELOW are mainly meant for user preferences and should not be used by applications e.g. for drawing attention to their dialogs (the Urgency hint should be used in that case, see the section called “Urgency”).

so window managers have no responsibility to respect applications which set this property directly (i.e by XChangeProperty) by themselves. this property can be changed only by sending a client message to the root window which window managers listens on.

i don't know how to do it in high-level gui toolkits like Qt, but here is how to do it in plain X11.(see EWMH spec, or _wnck_change_state for a sample implementation).

//file: test.c
//to build it, run
//shell> gcc test.c -lX11

#include <X11/Xlib.h>   

#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  */


// change a window's _NET_WM_STATE property so that it can be kept on top.
// @display: x11 display singleton.
// @xid    : the window to set on top.
Status x11_window_set_on_top (Display* display, Window xid)
{
    XEvent event;
    event.xclient.type = ClientMessage;
    event.xclient.serial = 0;
    event.xclient.send_event = True;
    event.xclient.display = display;
    event.xclient.window  = xid;
    event.xclient.message_type = XInternAtom (display, "_NET_WM_STATE", False);
    event.xclient.format = 32;

    event.xclient.data.l[0] = _NET_WM_STATE_ADD;
    event.xclient.data.l[1] = XInternAtom (display, "_NET_WM_STATE_ABOVE", False);
    event.xclient.data.l[2] = 0; //unused.
    event.xclient.data.l[3] = 0;
    event.xclient.data.l[4] = 0;

    return XSendEvent (display, DefaultRootWindow(display), False,
                       SubstructureRedirectMask|SubstructureNotifyMask, &event);
}

// a sample main function for testing.
// shell> ./a.out window_xid
int main (int argc, char** argv)
{
    Window xid = strtol (argv[1], NULL, 0); 
    Display* display = XOpenDisplay (NULL);

    x11_window_set_on_top (display, xid);

    XFlush (display); //for simplicity, no event loops here.

    XCloseDisplay (display);
}

also note that in some x11 environment(e.g. compiz), system menus are provided by a separate decorator program instead of the compositing window manager.

Autres conseils

In Go, this is how you do it:

import (
    "github.com/BurntSushi/xgb"
    "github.com/BurntSushi/xgb/xproto"
)

func (window *Window) AlwaysOnTop() {
    xid := xproto.Window(window.WinId())
    X, err := xgb.NewConn()
    if err != nil {
        log.Println(err)
        return
    }
    defer X.Close()

    state, err := xproto.InternAtom(X, false, uint16(len("_NET_WM_STATE")),
        "_NET_WM_STATE").Reply()
    if err != nil {
        log.Println(err)
        return
    }

    stateAbove, err := xproto.InternAtom(X, false,
        uint16(len("_NET_WM_STATE_ABOVE")), "_NET_WM_STATE_ABOVE").Reply()
    if err != nil {
        log.Println(err)
        return
    }

    evt := xproto.ClientMessageEvent{
        Window: xid,
        Format: 32,
        Type:   state.Atom,
        Data: xproto.ClientMessageDataUnionData32New([]uint32{
            _NET_WM_STATE_ADD,
            uint32(stateAbove.Atom),
            0,
            0,
            0,
        }),
    }

    err = xproto.SendEventChecked(X, false, xproto.Setup(X).DefaultScreen(X).Root,
        xproto.EventMaskSubstructureRedirect|xproto.EventMaskSubstructureNotify,
        string(evt.Bytes())).Check()
    if err != nil {
        log.Println(err)
    }
}

where window.WinId() is the native X11 handle of a window.

I hope i understood the problem correctly as you were trying to launch a QML view into "Always On Top" mode through a menu option.

I tried the following code, on windows, into my main and it worked for me to show the window always on top, so i believe the view object can be altered from menu option as well.

QApplication app(argc, argv);
QDeclarativeView viewer;
**viewer.setWindowFlags(Qt::WindowStaysOnTopHint);**
viewer.setSource(QUrl::fromLocalFile("TestView.qml"));
viewer.showNormal();
return app.exec();

Thanks, Zeeshan

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top