X11/Xlib: окно всегда сверху


Окно должно находиться поверх всех остальных окон. Возможно ли это каким-то образом с обычным x11/xlib? Поиск в Google слов "всегда на высоте" и "x11" / "xlib" не дал ничего полезного.

Я бы избегал таких инструментов, как GTK+, если бы это было возможно.

Я использую Ubuntu с рабочим столом gnome. В меню окна есть опция "всегда сверху". Это обеспечивается сервером 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;
    // ...
    }
}

Но eventhandling и raising почти никогда не выполняются, даже моя маска правильна?!

4 3

4 ответа:

Вы не хотите использовать XRaiseWindow (), чтобы попытаться остаться на вершине. Некоторые оконные менеджеры будут полностью игнорировать его. Для тех, кто этого не делает, рассмотрим, что произойдет, если это попытается сделать несколько приложений. Бум! Вот почему менеджер окон отвечает за укладку окон, а не за приложение.

Для этого необходимо использовать протоколы, определенные в расширенных подсказках оконного менеджера (EWMH), см.: http://www.freedesktop.org/wiki/Specifications/wm-spec

Конкретно здесь вы хотите _NET_WM_STATE_ABOVE именно так работает пункт меню "всегда сверху".

Если вы не используете инструментарий, вам нужно привыкнуть к поиску в исходном коде инструментария, чтобы понять, как это сделать. В этом случае вы можете посмотреть на функцию gdk_window_set_keep_above () в бэкэнде GTK+X11. Это покажет, как использовать подсказку _NET_WM_STATE_ABOVE.

#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
...
...
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" );
}

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

Я написал нечто подобное в Xlib много лет назад. Это несколько строк кода. Когда ваше окно частично скрыто, вы получаете событие VisibilityNotify, а затем вызываете XRaiseWindow. Следите за тем случаем, когда два ваших окна "всегда сверху" перекрываются.

Использовать кнопки фактического заголовка (http://www.actualtools.com/titlebuttons/) например. Это позволяет держать любые окна всегда сверху, закатывать, делать прозрачными и т. д..