Поведение Xlib и Firefox

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

Проблема заключается в том, что после запуска Firefox и добавления своего оформления кажется, что он работает нормально, но если, например, я пытаюсь нажать кнопку меню, окно (sub) не появится.

Похоже, что после клика событие ClientMessage запускается со следующими значениями:

Data: (null)
Data: _NET_WM_STATE_HIDDEN
Data: (null)
Data: (null)
Data: (null)

Теперь проблема в том, что я не знаю, как показать окно, какое окно. Я пробовал:

  • XRaiseWindow
  • XMapWindow
  • Я попытался получить временное окно и показать его

Но без успеха. Я не понимаю, что если это сообщение клиента генерируется подположением меню или нет.

Как показать окно, которое находится в _NET_WM_STATE_HIDDEN?

Еще одна странная проблема заключается в том, что после получения ClientMessage я всегда получаю 2 события UnMapNotify.

У меня также есть другой вопрос, если я хочу показать "File, Edit" menù (в Firefox он появляется, если я правильно помню, когда вы нажимаете кнопку Alt.

Возможно, Firefox создает дерево окон?

Это цикл, в котором я обрабатываю события:

while(1){
    XNextEvent(display, &local_event);
    switch(local_event.type){
        case ConfigureNotify:
            configure_notify_handler(local_event, display);
        break;
        case MotionNotify:
            motion_handler(local_event, display);
        break;
        case CreateNotify:
            cur_win = local_event.xcreatewindow.window;
            char *window_name;
            XFetchName(display, cur_win, &window_name);
            printf("Window name: %s\n", window_name);
            if(window_name!=NULL){
                if(!strcmp(window_name, "Parent")){
                    printf("Adding borders\n");
                    XSetWindowBorderWidth(display, cur_win, BORDER_WIDTH);
                }
                XFree(window_name);
            }
        break;
        case MapNotify:
            map_notify_handler(local_event,display, infos);
        break;
        case UnmapNotify: 
            printf("UnMapNotify\n");
        break;
        case DestroyNotify:
            printf("Destroy Event\n");
            destroy_notify_handler(local_event,display);
        break;
        case ButtonPress:
            printf("Event button pressed\n");
            button_handler(local_event, display, infos);
        break;
        case KeyPress:
            printf("Keyboard key pressed\n");
            keyboard_handler(local_event, display);
        break;
        case ClientMessage:
            printf("------------ClientMessage\n");
            printf("\tMessage: %s\n", XGetAtomName(display,local_event.xclient.message_type));
            printf("\tFormat: %d\n", local_event.xclient.format); 
            Atom *atoms = (Atom *)local_event.xclient.data.l;
            int i =0;
            for(i=0; i<=5; i++){
                printf("\t\tData %d: %s\n", i, XGetAtomName(display, atoms[i]));
            }
            int nchild;
            Window *child_windows;
            Window parent_window;
            Window root_window;
            XQueryTree(display, local_event.xclient.window, &root_window, &parent_window, &child_windows, &nchild);
            printf("\tNumber of childs: %d\n", nchild);
        break;
    }

Теперь в clientmessage на самом деле я просто пытаюсь собрать информацию, чтобы понять, что происходит. И то, что я вижу из приведенного выше кода, заключается в том, что окно, в котором было создано событие, содержит один дочерний элемент (опять же: это меню? Или нет?)

Код события MapNotify, где я добавляю оформление, следующий:

void map_notify_handler(XEvent local_event, Display* display, ScreenInfos infos){
    printf("----------Map Notify\n");
    XWindowAttributes win_attr;
    char *child_name;
    XGetWindowAttributes(display, local_event.xmap.window, &win_attr);
    XFetchName(display, local_event.xmap.window, &child_name);
    printf("\tAttributes: W: %d - H: %d - Name: %s - ID %lu\n", win_attr.width, win_attr.height, child_name, local_event.xmap.window);
    Window trans = None;    
    XGetTransientForHint(display, local_event.xmap.window, &trans); 
    printf("\tIs transient: %ld\n", trans);
    if(child_name!=NULL){
      if(strcmp(child_name, "Parent") && local_event.xmap.override_redirect == False){
        Window new_win = draw_window_with_name(display, RootWindow(display, infos.screen_num), "Parent", infos.screen_num, 
                           win_attr.x, win_attr.y, win_attr.width, win_attr.height+DECORATION_HEIGHT, 0, 
                           BlackPixel(display, infos.screen_num));
        XMapWindow(display, new_win);
        XReparentWindow(display,local_event.xmap.window, new_win,0, DECORATION_HEIGHT);
        set_window_item(local_event.xmap.window, new_win);
        XSelectInput(display, local_event.xmap.window, StructureNotifyMask);
        printf("\tParent window id: %lu\n", new_win);
        put_text(display, new_win, child_name, "9x15", 10, 10, BlackPixel(display,infos.screen_num), WhitePixel(display, infos.screen_num));
      }
    }
    XFree(child_name);
}

Теперь кто-нибудь может мне помочь с этими проблемами? К сожалению, я уже много раз искал Google, но безуспешно.

Подводя итог, мои проблемы - два: 1. Как показать подкачки из Firefox 2. Как показать меню File, Edit.

UPDATE

Я заметил что-то странное, проверяя Firefox с помощью xev, чтобы понять, какие события увольняются, чтобы показать приложение. Я видел, что использование Firefox в единстве и использование Firefox в другом ящике окна, уволенные события совершенно разные. В Unity у меня есть только:

  • ClientMessage
  • UnmapNotify

Вместо использования Firefox, например с xfce4, генерируемые xevents:

  • VisiblityNotify (более одного)
  • Событие Expose (более одного)

Но если я попытаюсь включить VisibilityChangeMask в моем wm, я получаю следующие события:

  • ConfigureNotify
  • ClientMessage
  • MapNotify
  • 2 UnMapNotify

ОБНОВЛЕНИЕ 2

Я попытался прочитать свойства XWMhints в окне ClientMessage (возможно, в мужском окне) и значения:

  • Для флагов 67 = InputHint, StateHint, WIndowGroupHint

  • Для исходного состояния NormalState

ОБНОВЛЕНИЕ 3

Я попытался посмотреть, как работает другой оконный менеджер, и я смотрел исходный код quietwm. Насколько я понимаю, когда событие ClientMessage приходит с сообщением _NET_WM_STATE, оно обновляет эти свойства, а в случае _NET_WM_STATE_HIDDEN оно очищает это свойство, и результат будет заключаться в том, что свойство будет удалено. Поэтому я попытался обновить свой код, чтобы удалить это свойство, но он все еще не работает. В любом случае соответствующий обновленный код в client_message_handler теперь выглядит следующим образом:

Atom *atoms = (Atom *)local_event.xclient.data.l;
int i =0;
for(i=0; i<=5; i++){
    printf("\t\tData %d: %s\n", i, XGetAtomName(display, atoms[i]));
    if(i==1){
        printf("\t Deleting Property: _NET_WM_STATE_HIDDEN \n");
        XDeleteProperty(display, cur_window, atoms[i]);
    }
}

Это только тест, и я уверен, что я = 1 в моем случае является свойством _NET_WM_STATE_HIDDEN.

Здесь ссылка на исходный код quietwm: https://github.com/chneukirchen/cwm/blob/linux/xevents.c

Итак, я все еще застрял в этом.

ОБНОВЛЕНИЕ 4

На самом деле я не знаю, помогает ли это, но я попытался прочитать атрибуты окна в событии MapNotify, а window map_state - IsViewable (2).

ОБНОВЛЕНИЕ 5

Я нашел аналогичную проблему здесь в SO, используя xlib с python: Xlib python: не может отображать меню firefox

Решение предлагает использовать XSetInputFocus, я попробовал это на своем обработчике XMapNotify:

XSetInputFocus(display, local_event.xmap.window, RevertToParent, CurrentTime);

Но это все равно не помогает, меню firefox все еще не появляется!! И у меня такая же проблема при щелчке правой кнопкой мыши.

ОБНОВЛЕНИЕ 6

Играя с событием xconfigurenotify и unmap, я обнаружил, что: Запрос Xconfigure имеет 2 поля окна: окно и выше, и когда значение xconfigurerequest.window совпадает с значением xunmap.window.

А также, что xconfigurerequest.above всегда меняется, но xconfigurerequest.window всегда одинаково во всех событиях.

Кажется, что xconfigurerequest.выше связано с тем, какое меню я пытаюсь открыть. Например:

  • если щелкнуть правой кнопкой мыши на странице, я получу идентификатор (всегда один и тот же для каждого последующего щелчка)
  • Если я right-clik на вкладке, указанное выше значение - это еще один
  • и то же самое произойдет, если я щелкнул левой кнопкой мыши по главному меню firefox

Не знаю, поможет ли это.

На самом деле не знаю Кто-нибудь понял?

Ответ 1

Использовать xtruss - простая в использовании программа трассировки протокола X


Обзор

Любой программист, привыкший писать программы в Linux или Unix-системах System V-типа, столкнулся с программой, известной как strace или truss, которая контролирует другую программу и создает подробный журнал каждого системного вызова, который делает программа, другими словами, все взаимодействия программы с ядром ОС. Это часто бесценный инструмент отладки и почти такой же хороший образовательный.

Когда это GUI-программа (вернее, поведение программы, связанное с GUI), которую вы хотите понять или отлаживать, уровень взаимодействия с ядром ОС редко бывает самым полезным. Более полезно, хотелось бы зарегистрировать все взаимодействия с сервером X таким же образом.

Уже существуют программы, которые сделают это. Я знаю Xmon и Xtrace. Но они, как правило, требуют много усилий для настройки: вам нужно запустить программу для создания сервера прослушивания, а затем вручную организовать целевую программу, чтобы связаться с ней, а не с реальным сервером, включая некоторую неудобную работу с xauth. В идеале вы хотите, чтобы трассировка операций X-X была такой же простой, как отслеживание ее системных вызовов ядра: вы хотели бы набрать команду так же просто, как аргументы аргумента strace, и все автоматически обрабатывать для вас.

Кроме того, вывод этих программ менее прост в чтении, чем мне хотелось бы, - что в значительной степени означает, что он меньше похож на strace, чем хотелось бы. strace имеет приятное свойство помещать каждый системный вызов и его возвращаемое значение в одну строку вывода, чтобы вы могли сразу увидеть, на что ответили ответы. Тем не менее, X-протокольные мониторы, как правило, строго следуют структуре протокола X, что означает, что каждый запрос и ответ печатаются с порядковым номером, и вы должны сопоставлять их по глазу.

Итак, эта страница представляет xtruss, мой собственный вклад в область протоколов X-протокола. Он имеет синтаксис командной строки, похожий на strace, в своем режиме по умолчанию, вы просто префикс "xtruss" в той же командной строке, которую вы бы запускали в любом случае, - и его выходной формат также больше похож на strace, помещая запросы и ответы на один и тот же линия выхода, где это возможно.

strace также поддерживает функцию привязки к уже запущенному процессу и отслеживание его с середины его запуска, когда что-то пойдет не так с длительным процессом, о котором вы не знали заранее, что вам понадобится проследить. xtruss поддерживает эту же функцию с помощью расширения X RECORD (если ваш X-сервер поддерживает его, какие современные X.Org); поэтому в этом режиме вы можете идентифицировать окно с помощью мыши (подобно стандартным программам, таким как xwininfo и xkill), а xtruss будет прикрепляться к клиентской программе X, которая владеет указанным вами окном, и начать отслеживать ее.


Описание

xtruss - это утилита, которая регистрирует все, что проходит между X-сервером и одной или несколькими X-клиентскими программами. В этом он похож на xmon (1), но предназначен для объединения базовой функции xmon с интерфейсом, гораздо более похожим на strace (1).

Как и xmon, xtruss в режиме по умолчанию работает, настроив прокси-сервер X, ожидая подключения к нему и перенаправляя их на реальный X-сервер. Однако, в отличие от xmon, вам не нужно разбираться ни с одним из них: нет необходимости запускать утилиту трассировки в одном терминале и вручную присоединять к ней процессы из другого, если вы действительно этого не хотите (в этом случае - P) сделает это). Основной способ использования - просто ввести xtruss, за которым следует командная строка вашей X-программы; xtruss автоматически позаботится о настройке новой программной среды, чтобы указать на ее прокси-сервер, и (также в отличие от xmon) также автоматически позаботится о авторизации X.

В качестве альтернативного способа использования вы также можете подключить xtruss к уже запущенному X-приложению, если вы не понимаете, что собираетесь отслеживать его до тех пор, пока он не будет запущен. Этот режим требует сотрудничества с сервером X - в частности, он не может работать, если сервер не поддерживает расширение протокола RECORD, но поскольку современные серверы X.Org действительно предоставляют это, это часто полезно.