【问题标题】:X11/Xlib: Window always on topX11/Xlib:窗口始终在顶部
【发布时间】:2011-05-19 17:10:50
【问题描述】:

一个窗口应该位于所有其他窗口的顶部。这对于普通的 x11/xlib 有可能吗?谷歌搜索“Always on top”和“x11”/“xlib”没有返回任何有用的信息。

如果可能的话,我会避免使用 GTK+ 之类的工具包。

我正在使用带有 gnome 桌面的 Ubuntu。在窗口菜单中,有一个选项“始终在顶部”。这是由 X 服务器还是窗口管理器提供的?如果是第二种情况,是否有一个可以为几乎任何 wm 调用的通用函数?或者如何以“X11-generic”的方式做到这一点?


编辑:我实现了 fizzer 的答案,现在有以下代码:
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;
    // ...
    }
}

但是即使我的面具是正确的,事件处理和引发也几乎永远不会被执行?!

【问题讨论】:

  • 几乎所有的低级窗口工具包都是用这两种语言中的一种或两种定义的,所以如果可以做到,那么可以用 C/C++ 完成。这不是一个真正的语言问题。
  • 改变了问题。确定这不是关于是否可以使用 c/c++,而是是否可以仅使用 x11/xlib 的最小绑定
  • 你有其他事件类型吗?
  • 另外,您的窗口是否位于顶层 - 即根窗口的子窗口?你只能提高一个相对于兄弟姐妹的窗口。
  • 是的,我确实得到了其他事件,并且胜利是根胜利的孩子。是因为我做了以下吗? XserverRegion region = XFixesCreateRegion( this->display, 0, 0 ); XFixesSetWindowShapeRegion(this->display, this->window, ShapeInput, 0, 0, region );

标签: user-interface x11 window-managers


【解决方案1】:
#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;
}

【讨论】:

  • 一个极好的解决方案,谢谢!我喜欢与 cmets 中的 GDK 进行比较!我很好奇你怎么没有设置xclient.send_event
  • XFlush 必须在XSendEvent 之后调用,否则不会将窗口设置为始终在顶部。
  • 迟到的评论,但 XSendEvent 会为你设置 xclient.send_event。
【解决方案2】:

您不想使用 XRaiseWindow() 来保持领先。一些窗口管理器会完全忽略它。对于那些不这样做的人,请考虑如果多个应用程序尝试这样做会发生什么。繁荣!这就是为什么窗口管理器负责堆叠窗口,而不是应用程序。

执行此操作的方法是使用扩展窗口管理器提示 (EWMH) 中定义的协议,请参阅:http://www.freedesktop.org/wiki/Specifications/wm-spec

具体来说,您需要 _NET_WM_STATE_ABOVE,这就是“始终位于顶部”菜单项的工作方式。

如果您不使用工具包,则需要习惯于在工具包源代码中进行清理以弄清楚如何做事。在这种情况下,您可以查看 GTK+ 的 X11 后端中的函数 gdk_window_set_keep_above()。这将展示如何使用 _NET_WM_STATE_ABOVE 提示。

【讨论】:

  • 谢谢。当然你不使用 XRaiseWindow() 是对的。也是我的想法。但是我之前已经看过 gdk_window_set_keep_above() 并且我尝试将 XEvent "_NET_WM_STATE_ABOVE" 发送到特定窗口。这根本没有做任何事情......使用 Compiz 和 Metacity。
  • 这里可能会犯一些错误,例如错误地获取 XSendEvent 的详细信息,或者在您应该设置属性时在尚未映射的窗口上使用事件。 WM 也可能会忽略某些语义窗口类型的消息。无论如何,如果您发布一个单一文件的可编译测试程序,人们可能会尝试进行诊断。
  • 嗨浩劫!我想知道你是否可以将我链接到 GDK 后端源代码,这样我就可以看到他们是如何处理gdk_window_set_keep_above 的。另外,如果您可以将我链接到 GTK 源代码,我也想查看gtk_window_set_abovedeveloper.gnome.org/gtk3/unstable/…
【解决方案3】:

很多年前我在 Xlib 中写过类似的东西。这是几行代码。当您的窗口部分被遮挡时,您会收到一个 VisibilityNotify 事件,然后调用 XRaiseWindow。请注意两个“始终位于顶部”窗口重叠的情况。

【讨论】:

  • 这不是一个好主意,因为只要有两个应用程序这样做,它们就会“争夺”顶部。在 EWMH 中有一个专门的提示。
  • 在您没有窗口管理器的极少数情况下,这种技术几乎是您所拥有的,但在这种情况下,您可能非常控制正在运行的内容,因此避免使用两个窗口争夺顶部要简单得多。
【解决方案4】:

例如使用实际标题按钮 (http://www.actualtools.com/titlebuttons/)。它允许将任何窗口始终保持在顶部、卷起、透明等。

【讨论】:

  • 这与 ms 窗口无关,也与标题栏中的可点击按钮无关。你完全没有抓住重点。
猜你喜欢
  • 1970-01-01
  • 2010-11-15
  • 1970-01-01
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
相关资源
最近更新 更多