c - Xlib 和 Firefox 行为

标签 c x11 xlib

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

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

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

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

现在的问题是我不知道如何显示窗口,哪个窗口。
我试过:
  • XRaiseWindow
  • XMapWindow
  • 我试图获取 transient 窗口并显示它

  • 但没有成功。我不明白的是,此客户端消息是否由菜单子(monad)窗口生成。

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

    另一个奇怪的问题是,收到ClientMessage后,总是收到2个UnMapNotify Events。

    我还有另一个问题,如果我想显示“文件,编辑”菜单(在 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;
        }
    

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

    我在其中添加装饰的 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);
    }
    

    现在有人可以帮我解决这些问题吗?不幸的是,我已经用谷歌搜索了很多次,但都没有成功。

    总结一下,我的问题有两个:
    1. 如何从 Firefox 显示子窗口
    2. 如何显示文件、编辑菜单。

    更新

    我注意到使用 xev 测试 Firefox 以了解为了显示应用程序而触发了哪些事件。我看到在 unity 中使用 Firefox 和在另一个窗口管理器中使用 Firefox,触发的事件完全不同。在 Unity 我只有:
  • 客户留言
  • 取消映射通知

  • 代替使用 Firefox,例如使用 xfce4,生成的 xevents 更多:
  • VisiblityNotify(不止一个)
  • 曝光事件(不止一个)

  • 但是,如果我尝试在 wm 中启用 VisibilityChangeMask,则会收到以下事件:
  • 配置通知
  • 客户留言
  • map 通知
  • 2 UnMapNotify

  • 更新 2

    我试图读取 ClientMessage 窗口(可能是菜单窗口)中的 XWMints 属性,其值为:
  • 对于标志 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]);
        }
    }
    

    这只是一个测试,我确定 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: cannot map firefox menus

    解决方案建议使用 XSetInputFocus,我在 XMapNotify 处理程序上尝试过:
    XSetInputFocus(display, local_event.xmap.window, RevertToParent, CurrentTime);
    

    但是还是没有用,firefox 菜单还是没有出现!!
    我右键单击也有同样的问题。

    更新 6

    玩 xconfigurenotify 事件和取消映射事件我发现:
    Xconfigure 请求有 2 个窗口字段:窗口及以上,以及当
    xconfigurerequest.window 值与 xunmap.window 值相同。

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

    似乎 xconfigurerequest.above 与我试图打开的菜单有关。例如:
  • 如果在页面上右键单击,我会得到一个 ID(每次后续单击始终相同)
  • 如果我右键单击一个选项卡,上面的值是另一个
  • 如果我左键单击 Firefox 主菜单,也会发生同样的情况

  • 仍然不知道这是否有帮助。

    真的不知道
    有人知道吗?

    最佳答案

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

    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;
        }
    }
    

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

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

    PS即使这是公认的答案,它的代码是用于xcb的,如果您需要xlib的代码,请在下面查看我的答案,使用适用于xlib的代码,它仅涵盖MapNotify事件

    关于c - Xlib 和 Firefox 行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31535560/

    相关文章:

    c - 从c中的输入字符串中删除字符

    c++ - 如何在Xlib应用程序中等待VSYNC?

    linux - 分配给使用 gtk_drawing_area_new 创建的 gtk 窗口的颜色图错误

    c++ - 使用 Xlib 在 Linux 上绘制图像问题

    清除带有桌面背景像素的 X11 窗口,并在其上放置带有透明像素的 XImage?

    c - 如何将文字数组作为函数的输入参数传递?

    c - 在 return 语句中使用带有参数的宏

    c++ - 如何将这个用于 OpenCV 的 MatExpr 的 C 包装器的指针输出转换回 MatExpr

    linux - 在 Linux 上捕获显示/监视器图像、发送键盘输入

    linux - 如何正确使用Openbox+xcompmgr?