8

我已经创建了一个无框架的 Qt/QML 窗口,我真的很想知道设置其“始终在顶部”系统菜单标志的任何编程方式。单击ALT+SPACE我可以调出无框窗口的系统菜单,通过单击“始终在顶部”选项,窗口确实始终位于顶部,但我还没有找到一种编程方式来做同样的事情。Qt.WindowStaysOnTopHint不起作用,尝试也wmctrl -r "window name" -b add,above不起作用,即使wmctrl对其他窗口有效。wmctrl不适用于我感兴趣的窗口显然与N/A机器名称列有关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

我也经历了这个过程,但对于用户询问,它对我也不起作用,同样的行为。已_NET_WM_STATE_ABOVE设置,但聚焦窗口,然后再次检查标志,它不再存在,仅在单击系统菜单时才具有粘性。

这是 QML:https ://gist.github.com/oblitum/8050586

相关askubuntu问题:https ://askubuntu.com/questions/394998

编辑

注意

在相关的 askubuntu 问题中,发现 wmctrl 上应该存在一个错误,用于通过名称定位某些窗口。使用wmctrl -i -r <window id> -b add,above也解决了这个问题。

4

3 回答 3

8

EWMH 规范明确指出:

_NET_WM_STATE_ABOVE 和 _NET_WM_STATE_BELOW 主要用于用户偏好,不应由应用程序使用,例如用于引起对其对话框的注意(在这种情况下应使用紧急提示,请参阅名为“紧急”的部分</a>)。

所以窗口管理器没有责任尊重那些自己直接设置这个属性的应用程序(即通过XChangeProperty)。只能通过向窗口管理器侦听的根窗口发送客户端消息来更改此属性。

我不知道如何在 Qt 等高级 gui 工具包中执行此操作,但这里是如何在普通 X11 中执行此操作。(请参阅 EWMH 规范或_wnck_change_state以获取示例实现)。

//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);
}

另请注意,在某些 x11 环境(例如 compiz)中,系统菜单由单独的装饰器程序提供,而不是由合成窗口管理器提供。

于 2013-12-26T03:18:40.337 回答
0

在 Go 中,你是这样做的:

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)
    }
}

window.WinId()窗口的原生 X11 句柄在哪里。

于 2013-12-31T17:51:25.307 回答
-1

我希望当您尝试通过菜单选项将 QML 视图启动到“始终在顶部”模式时,我正确理解了这个问题。

我在 Windows 上尝试了以下代码到我的 main 中,它对我来说总是在顶部显示窗口,所以我相信视图对象也可以从菜单选项中更改。

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

谢谢,泽山

于 2013-12-23T07:17:16.463 回答