我正在尝试创建一个小窗口管理器(只是为了好玩),但是我在处理由 Firefox 创建的窗口时遇到了问题(仅使用该应用程序,其他应用程序可以正常工作)
问题是,在我启动 Firefox 并添加我的装饰后,它似乎工作正常,但是例如,如果我尝试单击菜单按钮,则(子)窗口不会出现。
似乎发生的事情是在单击后,会使用以下值触发 ClientMessage 事件:
Data: (null)
Data: _NET_WM_STATE_HIDDEN
Data: (null)
Data: (null)
Data: (null)
现在的问题是我不知道如何显示窗口,哪个窗口。
我试过:
但没有成功。我不明白的是,此客户端消息是否由菜单子(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 更多:
但是,如果我尝试在 wm 中启用 VisibilityChangeMask,则会收到以下事件:
更新 2
我试图读取 ClientMessage 窗口(可能是菜单窗口)中的 XWMints 属性,其值为:
更新 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 与我试图打开的菜单有关。例如:
仍然不知道这是否有帮助。
真的不知道
有人知道吗?
最佳答案
这个问题很古老,但为了任何偶然发现它并寻找答案的人的利益,这里有一个编辑(切碎)示例,说明我如何根据上述提示解决这个问题:
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/