Xlib和Firefox的行为

Iva*_*van 61 c x11 xlib

我正在尝试创建一个小窗口管理器(只是为了好玩),但我在处理由Firefox创建的窗口时遇到问题(只有该应用程序,其他应用程序工作正常)

问题是,在我启动Firefox后,添加我的装饰,它似乎工作正常,但如果我尝试单击菜单按钮,则不会出现(子)窗口.

似乎发生的是,在单击之后,会使用以下值触发ClientMessage事件:

Data: (null)
Data: _NET_WM_STATE_HIDDEN
Data: (null)
Data: (null)
Data: (null)
Run Code Online (Sandbox Code Playgroud)

现在的问题是我不知道如何显示窗口,哪个窗口.我尝试过:

  • XRaiseWindow
  • XMapWindow
  • 我试图获得瞬态窗口并显示它

但没有成功.我不明白的是,如果此客户端消息是由菜单子窗口生成的.

我应该如何显示_NET_WM_STATE_HIDDEN中的窗口?

另一个奇怪的问题是,在收到ClientMessage后,我总是收到2个UnMapNotify事件.

我还有另一个问题,如果我想显示"文件,编辑"菜单(在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;
    }
Run Code Online (Sandbox Code Playgroud)

现在在客户端消息中我实际上只是想看看收集一些信息来了解正在发生的事情.我从上面的代码中可以看到,引发事件的窗口包含一个子节点(再次:是菜单?还是不是?)

我添加装饰的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);
}
Run Code Online (Sandbox Code Playgroud)

现在有人可以帮我解决这些问题吗?不幸的是,我已经多次使用Google搜索,但没有成功.

总而言之,我的问题是两个:1.如何显示来自Firefox 2的子窗口.如何显示文件,编辑菜单.

UPDATE

我注意到用xev测试Firefox的一些奇怪的事情,以了解为了显示应用程序而触发的事件.我看到使用Firefox统一,并在另一个窗口管理器中使用Firefox,所触发的事件完全不同.在Unity我只有:

  1. ClientMessage
  2. UnmapNotify

而是使用Firefox,例如使用xfce4,生成的xevents更多:

  1. VisiblityNotify(不止一个)
  2. 揭露事件(不止一个)

但是如果我尝试在我的wm中启用VisibilityChangeMask,我会收到以下事件:

  • ConfigureNotify
  • ClientMessage
  • MapNotify
  • 2 UnMapNotify

更新2

我试图在ClientMessage窗口(可能是menù窗口)中读取XWMhints属性,值为:

  • 对于标志67 = InputHint,StateHint,WIndowGroupHint

  • 对于初始状态NormalState

更新3

我试着看看另一个窗口管理器是如何工作的,我正在查看calmwm的源代码.我的理解是,当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]);
    }
}
Run Code Online (Sandbox Code Playgroud)

它只是一个测试,我确信在我的情况下i = 1是_NET_WM_STATE_HIDDEN属性.

这里有一个关于calmwm源代码的链接:https://github.com/chneukirchen/cwm/blob/linux/xevents.c

所以我仍然坚持到那一点.

更新4

真的我不知道它是否有帮助,但我试图在MapNotify事件中读取窗口属性,窗口map_state是IsViewable(2).

更新5

我在SO中发现了类似的问题,使用xlib和python:Xlib python:无法映射firefox菜单

解决方案建议使用XSetInputFocus,我在我的XMapNotify处理程序上尝试过:

XSetInputFocus(display, local_event.xmap.window, RevertToParent, CurrentTime);
Run Code Online (Sandbox Code Playgroud)

但它仍然没有帮助,firefox菜单仍然没有出现!右键单击我有同样的问题.

更新6

使用xconfigurenotify事件和unmap事件我发现:Xconfigure请求有2个窗口字段:window和above,以及xconfigurerequest.window值与xunmap.window值相同.

并且xconfigurerequest.above总是在变化,但xconfigurerequest.window在所有事件中始终是相同的.

似乎xconfigurerequest.above与我正在尝试打开的菜单有关.例如:

  • 如果右键单击页面,我会得到一个id(每次后续点击都一样)
  • 如果我在选项卡上右键,则上面的值是另一个值
  • 如果我左键单击firefox主菜单,也会发生同样的情况

仍然不知道这是否有帮助.

真的不知道有谁有任何想法?

小智 3

这个问题很古老,但为了任何偶然发现这个问题并寻找答案的人的利益,这里有一个编辑过的(切成碎片的)示例,说明我如何根据上面的提示解决这个问题:

while (event = xcb_poll_for_event(connection)) {
    uint8_t actual_event = event->response_type & 127;
    switch (actual_event) {
        case XCB_MAP_NOTIFY: ;
            xcb_map_notify_event_t *map_evt = (xcb_map_notify_event_t *)event;
            if (map_evt->override_redirect) {
                xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for(connection, map_evt->window);
                xcb_window_t transient_for = 0;
                xcb_icccm_get_wm_transient_for_reply(connection, cookie, &transient_for, NULL);
                if (transient_for) {
                    xcb_set_input_focus(connection, XCB_INPUT_FOCUS_POINTER_ROOT, transient_for, XCB_CURRENT_TIME);
                }
                xcb_flush(connection);
            }
            break;
        case XCB_CLIENT_MESSAGE: ;
            xcb_client_message_event_t *message_evt = (xcb_client_message_event_t *)event;
            xcb_get_atom_name_cookie_t name_cookie = xcb_get_atom_name(connection, message_evt->type);
            xcb_get_atom_name_reply_t *name_reply = xcb_get_atom_name_reply(connection, name_cookie, NULL);
            int length = xcb_get_atom_name_name_length(name_reply);
            char *atom_name = malloc(length + 1);
            strncpy(atom_name, xcb_get_atom_name_name(name_reply), length);
            atom_name[length] = '\0';
            free(atom_name);
            free(name_reply);

            if (message_evt->type == ewmh->_NET_WM_STATE) {
                xcb_atom_t atom = message_evt->data.data32[1];
                unsigned int action = message_evt->data.data32[0];
                xcb_get_atom_name_cookie_t name_cookie = xcb_get_atom_name(connection, atom);
                xcb_get_atom_name_reply_t *name_reply = xcb_get_atom_name_reply(connection, name_cookie, NULL);
                int length = xcb_get_atom_name_name_length(name_reply);
                char *atom_name = malloc(length + 1);
                strncpy(atom_name, xcb_get_atom_name_name(name_reply), length);
                atom_name[length] = '\0';
                if (action == XCB_EWMH_WM_STATE_REMOVE) {
                    if (atom == ewmh->_NET_WM_STATE_HIDDEN) {
                        xcb_delete_property(connection, message_evt->window, ewmh->_NET_WM_STATE_HIDDEN);
                    }
                }
                free(atom_name);
                free(name_reply);
            }
            break;
    }
}
Run Code Online (Sandbox Code Playgroud)

作为解释,要处理的重要事件是 MapNotify 和 ClientMessage,因为有两个主要事情需要注意,窗口必须根据请求删除其隐藏状态(xcb_delete_property 调用)以及瞬态的父窗口必须获得输入焦点(xcb_set_input_focus 调用;请注意,瞬态是瞬态的窗口才能获得焦点,而不是瞬态本身),否则 Firefox 将立即再次隐藏瞬态。

对于将瞬变堆叠在其父级之上似乎也很重要,因此 WM 应该尊重ConfigureRequest 事件。

PS 即使这是接受的答案,它的代码是针对 xcb 的,如果您需要 xlib 的代码,请检查下面我的答案,其中代码适用于 xlib,它确实只涵盖 MapNotify 事件