X11 Как восстановить / поднять другое окно приложения, используя xcb? - PullRequest
0 голосов
/ 23 января 2019

Я записываю список окон в свое приложение C, в котором отображаются все окна верхнего уровня, включая затененные, свернутые и другие рабочие столы.Я хотел бы восстановить не отображенные (свернутые) окна, поднять окно и переключиться на рабочий стол / рабочее пространство окна, когда оно выбрано.

В прошлом я писал что-то для достижения этой цели с помощью Xlib.Я использовал XSendEvent () для отправки события ClientMessage типа _NET_ACTIVE_WINDOW с последующим XMapRaised () ранее, и оно работало довольно хорошо, но не идеально.

Сейчас я переписываю приложение и решил использовать XCB для окнаперечислите код, а не Xlib и надеетесь создать лучшую и более эффективную реализацию.XCB не имеет эквивалента XMapRaised (), и xcb_map_window (), похоже, не работает для меня.Я нашел достаточное количество документации по созданию и настройке новых окон, но очень мало полезного для реализации оконного менеджера или служебных программ, таких как пейджеры, значки, панели задач и т. Д. Сгенерированная документация для XCB довольно расплывчата в отношении того, чтонекоторые функции на самом деле делают так же.Если кто-нибудь знает о дополнительной документации, которая может быть полезна для этого, это тоже было бы здорово.

РЕДАКТИРОВАТЬ:

Я продублировал часть старого кода, который использует Xlib, в небольшойутилита, и она на самом деле делает большую часть того, что я хочу, и, кажется, работает согласованно:

#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>

int main(int argc, char **argv) {
    Display *display = XOpenDisplay("");
    Window rootwin = XDefaultRootWindow(display);
    if (argc < 2)
    {
        printf("usage: %s windowid\n", argv[0]);
        return 0;
    }
    Window window = (Window)strtoul(argv[1], NULL, 0);
    printf("switch to window: 0x%lx\n", window);
    Atom ActiveWindowAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", False);
    XEvent xev;
    xev.xclient.type = ClientMessage;
    xev.xclient.window = window;
    xev.xclient.message_type = ActiveWindowAtom;
    xev.xclient.format = 32;
    xev.xclient.data.l[0] = 1U;
    xev.xclient.data.l[1] = 1U;
    xev.xclient.data.l[2] = 0U;
    xev.xclient.data.l[3] = 0U;
    xev.xclient.data.l[4] = 0U;
    XSendEvent(display, rootwin, False, SubstructureRedirectMask, &xev);
    XMapRaised(display, window);
    XCloseDisplay(display);
    return 0;
}

... и затем я распаковал исходники libX11 и подтвердил, что она просто оборачивает функции XCB, ноОболочки используют некоторые структуры данных libX11 вместо тех, которые определены в XCB, и используется так много макросов, что трудно точно понять, что происходит.Это эквивалент XCB, который я придумал, и он не работает:

#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>
#include "atom_cache.h"

int main(int argc, char **argv) {
    xcb_connection_t *connection;
    const xcb_setup_t *setup;
    xcb_screen_iterator_t screen_iter;
    xcb_screen_t *screen;
    xcb_window_t rootwin, window;
    xcb_void_cookie_t void_cookie;
    xcb_client_message_event_t client_message_event;

    if (argc < 2)
    {
        printf("usage: %s windowid\n", argv[0]);
        return 0;
    }

    window = (xcb_window_t)strtoul(argv[1], NULL, 0);
    printf("switch to window: 0x%x\n", window);

    connection = xcb_connect(NULL, NULL);
    setup = xcb_get_setup(connection);
    screen_iter = xcb_setup_roots_iterator(setup);
    screen = screen_iter.data;
    rootwin = screen->root;

    // send _net_active_window request
    client_message_event.response_type = XCB_CLIENT_MESSAGE;
    client_message_event.format = 32;
    client_message_event.sequence = 0;
    client_message_event.window = window;
    client_message_event.type = get_atom(connection, "_NET_ACTIVE_WINDOW");
    client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
    client_message_event.data.data32[1] = 1UL; // timestamp
    client_message_event.data.data32[2] = 0UL; // my currently active window?
    client_message_event.data.data32[3] = 0UL;
    client_message_event.data.data32[4] = 0UL;

    void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);

    uint32_t values[] = { XCB_STACK_MODE_ABOVE };
    xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
    xcb_map_window(connection, window);

    xcb_flush(connection);
    xcb_disconnect(connection);
    return 0;
}

Я все еще пытаюсь выяснить это.

1 Ответ

0 голосов
/ 26 января 2019

Кажется, что код, который я разместил в конце моего вопроса, был верным, если не считать небольшую ошибку в atom_cache. Я также изменил порядок отображения окна, поднял окно и сделал его активным окном. Я думаю, что, возможно, я должен опубликовать эти решения на GitHub в качестве примеров XCB. Я написал пару таких маленьких утилит для взаимодействия с xserver и оконным менеджером, как это. Может быть, они будут полезны другим ...

xcb_switchto.c:

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <xcb/xcb.h>

int main(int argc, char **argv) {
    xcb_connection_t *connection;
    xcb_window_t rootwin, window;
    xcb_intern_atom_cookie_t atom_cookie;
    xcb_intern_atom_reply_t *atom_reply;
    xcb_atom_t net_active_window;
    xcb_query_tree_cookie_t qtree_cookie;
    xcb_query_tree_reply_t *qtree_reply;
    xcb_void_cookie_t void_cookie;
    xcb_client_message_event_t client_message_event;
    xcb_generic_error_t *err;

    if (argc < 2)
    {
        printf("usage: %s windowid\n", argv[0]);
        return EXIT_FAILURE;
    }

    window = (xcb_window_t)strtoul(argv[1], NULL, 0);
    printf("switch to window: 0x%x\n", window);

    // connect to X server
    connection = xcb_connect(NULL, NULL);

    // get _NET_ACTIVE_WINDOW atom from X server
    atom_cookie = xcb_intern_atom(connection, 0, 18, "_NET_ACTIVE_WINDOW");
    if (atom_reply = xcb_intern_atom_reply(connection, atom_cookie, &err)) {
        net_active_window = atom_reply->atom;
        free(atom_reply);
    } else if (err) {
        printf("xcb_intern_atom failed with error code %d\n", err->error_code);
        return EXIT_FAILURE;
    }

    // get the window's root window.  can there really be more than one?
    qtree_cookie = xcb_query_tree(connection, window);
    if (qtree_reply = xcb_query_tree_reply(connection, qtree_cookie, &err)) {
        rootwin = qtree_reply->root;
    } else if (err) {
        printf("xcb_query_tree failed with error code %d\n", err->error_code);
        return EXIT_FAILURE;
    }

    // map the window first
    xcb_map_window(connection, window);

    // now raise (restack) the window
    uint32_t values[] = { XCB_STACK_MODE_ABOVE };
    xcb_configure_window(connection, window, XCB_CONFIG_WINDOW_STACK_MODE, values);

    // and, finally, make it the active window
    client_message_event.response_type = XCB_CLIENT_MESSAGE;
    client_message_event.format = 32;
    client_message_event.sequence = 0;
    client_message_event.window = window;
    client_message_event.type = net_active_window;
    client_message_event.data.data32[0] = 1UL; // source: 1=application 2=pager
    client_message_event.data.data32[1] = 1UL; // timestamp
    client_message_event.data.data32[2] = 0UL; // currently active window (none)
    client_message_event.data.data32[3] = 0UL;
    client_message_event.data.data32[4] = 0UL;
    void_cookie = xcb_send_event(connection, 1, rootwin, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&client_message_event);

    // probably unnecessary
    xcb_flush(connection);
    // close the connection
    xcb_disconnect(connection);
    return EXIT_SUCCESS;
}

Здесь есть небольшая проблема, связанная с использованием 1 для отметки времени. Вероятно, было бы лучше запросить в окне _NET_WM_USER_TIME и использовать это значение, поскольку иногда оно игнорируется при использовании 1 каждый раз. Я не уверен, как вы могли бы иметь более одного корневого окна, но я все же подумал, что лучше спросить сервер, на каком корневом окне оно находится. Может быть, многоголовый дисплей не xinerama ... В любом случае, проблема решена.

...