void schedule_window(xcb_window_t win) { coordinates_t loc; uint8_t override_redirect = 0; xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL); if (wa != NULL) { override_redirect = wa->override_redirect; free(wa); } if (override_redirect || locate_window(win, &loc)) { return; } /* ignore pending windows */ for (pending_rule_t *pr = pending_rule_head; pr != NULL; pr = pr->next) { if (pr->win == win) { return; } } rule_consequence_t *csq = make_rule_conquence(); apply_rules(win, csq); if (!schedule_rules(win, csq)) { manage_window(win, csq, -1); free(csq); } }
void enter_notify(xcb_generic_event_t *evt) { xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt; xcb_window_t win = e->event; PRINTF("enter notify %X %d %d\n", win, e->mode, e->detail); if (e->mode != XCB_NOTIFY_MODE_NORMAL || (mon->desk->focus != NULL && mon->desk->focus->client->window == win)) { return; } xcb_get_window_attributes_reply_t *wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, motion_recorder), NULL); if (wa == NULL) { return; } if (wa->map_state == XCB_MAP_STATE_UNMAPPED) { enable_motion_recorder(); } else { disable_motion_recorder(); } }
static void nextwin (void) { xcb_query_tree_reply_t *r; xcb_window_t *c,t = 0; xcb_get_window_attributes_cookie_t ac; xcb_get_window_attributes_reply_t *ar; r = xcb_query_tree_reply(conn, xcb_query_tree(conn, scr->root), 0); if (!r || !r->children_len) return; c = xcb_query_tree_children(r); for (unsigned int i=0; i < r->children_len; i++) { ac = xcb_get_window_attributes(conn, c[i]); ar = xcb_get_window_attributes_reply(conn, ac, NULL); if (ar && ar->map_state == XCB_MAP_STATE_VIEWABLE) { if (!(ar->override_redirect || c[i] == (*focuswin))) { t = c[i]; } break; } } if (t) { focus(t, ACTIVE); center_pointer(t); } free(r); }
static xcb_visualtype_t * get_visualtype_for_window(xcb_connection_t *conn, xcb_window_t window, unsigned *depth) { xcb_query_tree_cookie_t tree_cookie; xcb_get_window_attributes_cookie_t attrib_cookie; xcb_query_tree_reply_t *tree; xcb_get_window_attributes_reply_t *attrib; tree_cookie = xcb_query_tree(conn, window); attrib_cookie = xcb_get_window_attributes(conn, window); tree = xcb_query_tree_reply(conn, tree_cookie, NULL); attrib = xcb_get_window_attributes_reply(conn, attrib_cookie, NULL); if (attrib == NULL || tree == NULL) { free(attrib); free(tree); return NULL; } xcb_window_t root = tree->root; xcb_visualid_t visual_id = attrib->visual; free(attrib); free(tree); xcb_screen_t *screen = get_screen_for_root(conn, root); if (screen == NULL) return NULL; return screen_get_visualtype(screen, visual_id, depth); }
void X11XRenderBackend::init(bool createOverlay) { if (m_front != XCB_RENDER_PICTURE_NONE) xcb_render_free_picture(connection(), m_front); bool haveOverlay = createOverlay ? m_overlayWindow->create() : (m_overlayWindow->window() != XCB_WINDOW_NONE); if (haveOverlay) { m_overlayWindow->setup(XCB_WINDOW_NONE); ScopedCPointer<xcb_get_window_attributes_reply_t> attribs(xcb_get_window_attributes_reply(connection(), xcb_get_window_attributes_unchecked(connection(), m_overlayWindow->window()), NULL)); if (!attribs) { setFailed("Failed getting window attributes for overlay window"); return; } m_format = XRenderUtils::findPictFormat(attribs->visual); if (m_format == 0) { setFailed("Failed to find XRender format for overlay window"); return; } m_front = xcb_generate_id(connection()); xcb_render_create_picture(connection(), m_front, m_overlayWindow->window(), m_format, 0, NULL); } else { // create XRender picture for the root window m_format = XRenderUtils::findPictFormat(defaultScreen()->root_visual); if (m_format == 0) { setFailed("Failed to find XRender format for root window"); return; // error } m_front = xcb_generate_id(connection()); const uint32_t values[] = {XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS}; xcb_render_create_picture(connection(), m_front, rootWindow(), m_format, XCB_RENDER_CP_SUBWINDOW_MODE, values); } createBuffer(); }
int handle_map_request_event(void *data, xcb_connection_t *c, xcb_map_request_event_t *event) { xcb_get_window_attributes_cookie_t win_attrs_cookie; xcb_get_window_attributes_reply_t *win_attrs_reply; win_attrs_cookie = xcb_get_window_attributes_unchecked(c, event->window); win_attrs_reply = xcb_get_window_attributes_reply(c, win_attrs_cookie, NULL); if (!win_attrs_reply) { fprintf(stderr, "map request: failed to get window attributes\n"); return -1; } if (win_attrs_reply->override_redirect) { fprintf(stderr, "map request: window has override redirect set - ignoring map request\n"); return 0; } client_t *client = NULL; client = find_client(event->window); if (!client) client = manage_window(event->window); xcb_map_window(c, event->window); run_arrange_hook(); free(win_attrs_reply); return 0; }
/* * This sets up the DAMAGE extentsion to receive damage events on all * child windows * */ static void set_up_damage_notifications(xcb_connection_t *conn, xcb_screen_t *scr) { xcb_damage_query_version_unchecked(conn, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION); dam_ext_data = xcb_get_extension_data(conn, &xcb_damage_id); xcb_query_tree_reply_t *reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, scr->root), NULL); xcb_window_t *children = xcb_query_tree_children(reply); xcb_get_window_attributes_cookie_t *attribs = (xcb_get_window_attributes_cookie_t *)malloc( sizeof(xcb_get_window_attributes_cookie_t) * reply->children_len); if (!attribs) { errx(EXIT_FAILURE, "Failed to allocate memory"); } for (int i = 0; i < reply->children_len; ++i) { attribs[i] = xcb_get_window_attributes_unchecked(conn, children[i]); } for (int i = 0; i < reply->children_len; ++i) { /* Get attributes to check if input-only window */ xcb_get_window_attributes_reply_t *attrib = xcb_get_window_attributes_reply(conn, attribs[i], NULL); create_damage(conn, children[i], attrib); } free(attribs); free(reply); }
static MetaGroup* meta_group_new (MetaX11Display *x11_display, Window group_leader) { MetaGroup *group; #define N_INITIAL_PROPS 3 Atom initial_props[N_INITIAL_PROPS]; int i; g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props)); group = g_new0 (MetaGroup, 1); group->x11_display = x11_display; group->windows = NULL; group->group_leader = group_leader; group->refcount = 1; /* owned by caller, hash table has only weak ref */ xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay); xcb_generic_error_t *e; g_autofree xcb_get_window_attributes_reply_t *attrs = xcb_get_window_attributes_reply (xcb_conn, xcb_get_window_attributes (xcb_conn, group_leader), &e); if (e) return NULL; const uint32_t events[] = { attrs->your_event_mask | XCB_EVENT_MASK_PROPERTY_CHANGE }; xcb_change_window_attributes (xcb_conn, group_leader, XCB_CW_EVENT_MASK, events); if (x11_display->groups_by_leader == NULL) x11_display->groups_by_leader = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); g_assert (g_hash_table_lookup (x11_display->groups_by_leader, &group_leader) == NULL); g_hash_table_insert (x11_display->groups_by_leader, &group->group_leader, group); /* Fill these in the order we want them to be gotten */ i = 0; initial_props[i++] = x11_display->atom_WM_CLIENT_MACHINE; initial_props[i++] = x11_display->atom__NET_WM_PID; initial_props[i++] = x11_display->atom__NET_STARTUP_ID; g_assert (N_INITIAL_PROPS == i); meta_group_reload_properties (group, initial_props, N_INITIAL_PROPS); meta_topic (META_DEBUG_GROUPS, "Created new group with leader 0x%lx\n", group->group_leader); return group; }
/** The map request event handler. * \param ev The event. */ static void event_handle_maprequest(xcb_map_request_event_t *ev) { client_t *c; xcb_get_window_attributes_cookie_t wa_c; xcb_get_window_attributes_reply_t *wa_r; xcb_get_geometry_cookie_t geom_c; xcb_get_geometry_reply_t *geom_r; wa_c = xcb_get_window_attributes_unchecked(globalconf.connection, ev->window); if(!(wa_r = xcb_get_window_attributes_reply(globalconf.connection, wa_c, NULL))) return; if(wa_r->override_redirect) goto bailout; if(xembed_getbywin(&globalconf.embedded, ev->window)) { xcb_map_window(globalconf.connection, ev->window); xembed_window_activate(globalconf.connection, ev->window); } else if((c = client_getbywin(ev->window))) { /* Check that it may be visible, but not asked to be hidden */ if(client_on_selected_tags(c) && !c->hidden) { lua_State *L = globalconf_get_lua_State(); luaA_object_push(L, c); client_set_minimized(L, -1, false); lua_pop(L, 1); /* it will be raised, so just update ourself */ client_raise(c); } } else { geom_c = xcb_get_geometry_unchecked(globalconf.connection, ev->window); if(!(geom_r = xcb_get_geometry_reply(globalconf.connection, geom_c, NULL))) { goto bailout; } client_manage(ev->window, geom_r, wa_r); p_delete(&geom_r); } bailout: p_delete(&wa_r); }
uint32_t _xcb_get_event_mask(xcb_connection_t* conn, xcb_window_t window) { if (window == XCB_NONE) { return 0; } xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(conn, window); xcb_get_window_attributes_reply_t* reply = xcb_get_window_attributes_reply(conn, cookie, NULL); if (!reply) { return 0; } uint32_t your_event_mask = reply->your_event_mask; free(reply); return your_event_mask; }
/* Generate a XCWM_EVENT_WINDOW_CREATE event for all existing mapped top-level windows when we start */ static void _xcwm_windows_adopt(xcwm_context_t *context, xcwm_event_cb_t callback_ptr) { xcb_query_tree_cookie_t tree_cookie = xcb_query_tree(context->conn, context->root_window->window_id); xcb_query_tree_reply_t *reply = xcb_query_tree_reply(context->conn, tree_cookie, NULL); if (NULL == reply) { return; } int len = xcb_query_tree_children_length(reply); xcb_window_t *children = xcb_query_tree_children(reply); int i; for (i = 0; i < len; i ++) { xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(context->conn, children[i]); xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(context->conn, cookie, NULL); if (!attr) { fprintf(stderr, "Couldn't get attributes for window 0x%08x\n", children[i]); continue; } if (attr->map_state == XCB_MAP_STATE_VIEWABLE) { printf("window 0x%08x viewable\n", children[i]); xcwm_window_t *window = _xcwm_window_create(context, children[i], context->root_window->window_id); if (!window) { continue; } _xcwm_window_composite_pixmap_update(window); xcwm_event_t return_evt; return_evt.window = window; return_evt.event_type = XCWM_EVENT_WINDOW_CREATE; callback_ptr(&return_evt); } else { printf("window 0x%08x non-viewable\n", children[i]); } free(attr); } free(reply); }
static void handle_map_notify(xcb_map_notify_event_t *event) { if (fuzzy) { /* Create damage objects for new windows */ xcb_get_window_attributes_reply_t *attribs = xcb_get_window_attributes_reply(conn, xcb_get_window_attributes(conn, event->window), NULL); create_damage(conn, event->window, attribs); } if (!dont_fork) { /* After the first MapNotify, we never fork again. */ dont_fork = true; /* In the parent process, we exit */ if (fork() != 0) exit(0); ev_loop_fork(EV_DEFAULT); } }
/* * Check if window is viewable */ static Bool Window_Is_Viewable(xcb_connection_t * dpy, xcb_window_t win) { Bool ok = False; xcb_get_window_attributes_cookie_t attr_cookie; xcb_get_window_attributes_reply_t *xwa; attr_cookie = xcb_get_window_attributes (dpy, win); xwa = xcb_get_window_attributes_reply (dpy, attr_cookie, NULL); if (xwa) { ok = (xwa->_class == XCB_WINDOW_CLASS_INPUT_OUTPUT) && (xwa->map_state == XCB_MAP_STATE_VIEWABLE); free (xwa); } return ok; }
int add_winvec(struct window **list, xcb_window_t wid[], int len) { xcb_get_window_attributes_cookie_t ack[len]; xcb_get_geometry_cookie_t gck[len]; struct window *w; int i, n = 0; for (i = 0; i < len; ++i) { ack[i] = xcb_get_window_attributes_unchecked(X, wid[i]); gck[i] = xcb_get_geometry_unchecked(X, wid[i]); } for (i = 0; i < len; ++i) { xcb_get_window_attributes_reply_t *ar; xcb_get_geometry_reply_t *gr; if ((w = add_win(list, wid[i])) != NULL) { ar = xcb_get_window_attributes_reply(X, ack[i], NULL); gr = xcb_get_geometry_reply(X, gck[i], NULL); if (ar && gr) { w->visual = ar->visual; w->_class = ar->_class; w->map_state = ar->map_state; w->x = gr->x; w->y = gr->y; w->width = gr->width; w->height = gr->height; w->border_width = gr->border_width; free(ar); free(gr); ++n; } else { if (ar) free(ar); if (gr) free(gr); remove_win(list, w); continue; } } } debugf("add_winvec: added %d of %d windows to stack\n", n, len); return n; }
// === WindowState() === LXCB::WINDOWSTATE LXCB::WindowState(WId win){ if(DEBUG){ qDebug() << "XCB: WindowState()"; } if(win==0){ return IGNORE; } xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win); if(cookie.sequence == 0){ return IGNORE; } xcb_ewmh_get_atoms_reply_t states; WINDOWSTATE cstate = IGNORE; //First Check for special states (ATTENTION in particular); if( 1 == xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &states, NULL) ){ for(unsigned int i=0; i<states.atoms_len; i++){ if(states.atoms[i] == EWMH._NET_WM_STATE_DEMANDS_ATTENTION){ cstate = ATTENTION; break; } //nothing more urgent - stop here else if(states.atoms[i] == EWMH._NET_WM_STATE_HIDDEN){ cstate = INVISIBLE; } } } //Now check to see if the window is the active one if(cstate == IGNORE){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_active_window_unchecked(&EWMH, 0); xcb_window_t actwin; if(1 == xcb_ewmh_get_active_window_reply(&EWMH, cookie, &actwin, NULL) ){ if(actwin == win){ cstate = ACTIVE; } } } //Now check for ICCCM Urgency hint (not sure if this is still valid with EWMH instead) /*if(cstate == IGNORE){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(QX11Info::connection(), win); xcb_icccm_wm_hints_t hints; if( 1== xcb_icccm_get_wm_hints_reply(QX11Info::connection(), cookie, &hints, NULL) ){ if(xcb_icccm_wm_hints_get_urgency(hints) ){ cstate = ATTENTION; }; } }*/ //Now check for standard visible/invisible attribute (current mapping state) if(cstate == IGNORE){ xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), win); xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL); if(attr!=0){ if(attr->map_state==XCB_MAP_STATE_VIEWABLE){ cstate = VISIBLE; } else{ cstate = INVISIBLE; } free(attr); } } return cstate; }
static void handle_map_request(xcb_map_request_event_t *e) { xcb_get_window_attributes_reply_t *reply; struct client_t *c; struct workspace_t *ws; NIL_LOG("event: map request win=%d", e->window); reply = xcb_get_window_attributes_reply(nil_.con, xcb_get_window_attributes_unchecked(nil_.con, e->window), 0); if (!reply) { NIL_ERR("no reply %d", e->window); return; } if (reply->override_redirect) { NIL_ERR("override_redirect %d", e->window); free(reply); return; } free(reply); c = find_client(e->window, &ws); if (!c) { NIL_ERR("no client %d", e->window); return; } init_client(c); NIL_SET_FLAG(c->flags, CLIENT_DISPLAY); if (!NIL_HAS_FLAG(c->flags, CLIENT_FLOAT)) { /* only rearrange if it's not float */ arrange_ws(ws); } if (check_client_size(c)) { /* fix window size if needed */ update_client_geom(c); } config_client(c); if (ws == &nil_.ws[nil_.ws_idx]) { xcb_map_window(nil_.con, c->win); xcb_set_input_focus(nil_.con, XCB_INPUT_FOCUS_POINTER_ROOT, c->win, XCB_CURRENT_TIME); } xcb_flush(nil_.con); }
void FdoSelectionManager::addDamageWatch(xcb_window_t client) { qCDebug(SNIPROXY) << "adding damage watch for " << client; xcb_connection_t *c = QX11Info::connection(); const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client); auto damageId = xcb_generate_id(c); m_damageWatches[client] = damageId; xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY); QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> attr(xcb_get_window_attributes_reply(c, attribsCookie, Q_NULLPTR)); uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY; if (!attr.isNull()) { events = events | attr->your_event_mask; } // the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem). // if we would remove the event mask again, other areas will break. xcb_change_window_attributes(c, client, XCB_CW_EVENT_MASK, &events); }
/** Scan X to find windows to manage. */ static void scan(xcb_query_tree_cookie_t tree_c) { int i, tree_c_len; xcb_query_tree_reply_t *tree_r; xcb_window_t *wins = NULL; xcb_get_window_attributes_reply_t *attr_r; xcb_get_geometry_reply_t *geom_r; long state; tree_r = xcb_query_tree_reply(globalconf.connection, tree_c, NULL); if(!tree_r) return; /* Get the tree of the children windows of the current root window */ if(!(wins = xcb_query_tree_children(tree_r))) fatal("cannot get tree children"); tree_c_len = xcb_query_tree_children_length(tree_r); xcb_get_window_attributes_cookie_t attr_wins[tree_c_len]; xcb_get_property_cookie_t state_wins[tree_c_len]; for(i = 0; i < tree_c_len; i++) { attr_wins[i] = xcb_get_window_attributes_unchecked(globalconf.connection, wins[i]); state_wins[i] = xwindow_get_state_unchecked(wins[i]); } xcb_get_geometry_cookie_t *geom_wins[tree_c_len]; for(i = 0; i < tree_c_len; i++) { attr_r = xcb_get_window_attributes_reply(globalconf.connection, attr_wins[i], NULL); state = xwindow_get_state_reply(state_wins[i]); if(!attr_r || attr_r->override_redirect || attr_r->map_state == XCB_MAP_STATE_UNMAPPED || state == XCB_ICCCM_WM_STATE_WITHDRAWN) { geom_wins[i] = NULL; p_delete(&attr_r); continue; } p_delete(&attr_r); /* Get the geometry of the current window */ geom_wins[i] = p_alloca(xcb_get_geometry_cookie_t, 1); *(geom_wins[i]) = xcb_get_geometry_unchecked(globalconf.connection, wins[i]); } for(i = 0; i < tree_c_len; i++) { if(!geom_wins[i] || !(geom_r = xcb_get_geometry_reply(globalconf.connection, *(geom_wins[i]), NULL))) continue; client_manage(wins[i], geom_r, true); p_delete(&geom_r); } p_delete(&tree_r); }
/* * List all hidden windows. * */ int findhidden(void) { xcb_query_tree_reply_t *reply; int i; int len; xcb_window_t *children; xcb_get_window_attributes_reply_t *attr; uint32_t state; xcb_get_property_cookie_t cookie; xcb_icccm_get_text_property_reply_t prop; xcb_generic_error_t *error; /* Get all children. */ reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, screen->root), 0); if (NULL == reply) { return -1; } len = xcb_query_tree_children_length(reply); children = xcb_query_tree_children(reply); /* List all hidden windows on this root. */ for (i = 0; i < len; i ++) { attr = xcb_get_window_attributes_reply( conn, xcb_get_window_attributes(conn, children[i]), NULL); if (!attr) { fprintf(stderr, "Couldn't get attributes for window %d.", children[i]); continue; } /* * Don't bother windows in override redirect mode. * * This mode means they wouldn't have been reported to us * with a MapRequest if we had been running, so in the * normal case we wouldn't have seen them. */ if (!attr->override_redirect) { state = get_wm_state(children[i]); if (state == XCB_ICCCM_WM_STATE_ICONIC) { /* * Example names: * * _NET_WM_ICON_NAME(UTF8_STRING) = 0x75, 0x72, 0x78, * 0x76, 0x74 WM_ICON_NAME(STRING) = "urxvt" * _NET_WM_NAME(UTF8_STRING) = 0x75, 0x72, 0x78, 0x76, * 0x74 WM_NAME(STRING) = "urxvt" */ cookie = xcb_icccm_get_wm_icon_name(conn, children[i]); xcb_icccm_get_wm_icon_name_reply(conn, cookie, &prop, &error); prop.name[prop.name_len] = '\0'; if (printcommand) { /* FIXME: Need to escape : in prop.name. */ printf("'%s':'xdotool windowmap 0x%x windowraise 0x%x'\n", prop.name, children[i], children[i]); } else { puts(prop.name); } } } /* if not override redirect */ free(attr); } /* for */ free(reply); return 0; }
static int CreateWindowAndContext( Display* display, xcb_connection_t* connection, int default_screen, xcb_screen_t* screen, int width, int height, ae3d::WindowCreateFlags flags ) { GLXFBConfig* fb_configs = nullptr; int num_fb_configs = 0; fb_configs = glXGetFBConfigs( display, default_screen, &num_fb_configs ); if (!fb_configs || num_fb_configs == 0) { std::cerr << "glXGetFBConfigs failed." << std::endl; return -1; } /* Select first framebuffer config and query visualID */ int visualID = 0; GLXFBConfig fb_config = fb_configs[ 0 ]; glXGetFBConfigAttrib( display, fb_config, GLX_VISUAL_ID, &visualID ); GLXContext context = glXCreateNewContext( display, fb_config, GLX_RGBA_TYPE, nullptr, True ); if (!context) { std::cerr << "glXCreateNewContext failed." << std::endl; return -1; } /* Create XID's for colormap and window */ xcb_colormap_t colormap = xcb_generate_id( connection ); WindowGlobal::window = xcb_generate_id( connection ); WindowGlobal::windowWidth = width == 0 ? screen->width_in_pixels : width; WindowGlobal::windowHeight = height == 0 ? screen->height_in_pixels : height; xcb_create_colormap( connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visualID ); const uint32_t eventmask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION; const uint32_t valuelist[] = { eventmask, colormap, 0 }; const uint32_t valuemask = XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; xcb_create_window( connection, XCB_COPY_FROM_PARENT, WindowGlobal::window, screen->root, 0, 0, WindowGlobal::windowWidth, WindowGlobal::windowHeight, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, visualID, valuemask, valuelist ); xcb_map_window( connection, WindowGlobal::window ); if ((flags & ae3d::WindowCreateFlags::Fullscreen) != 0) { WindowGlobal::EWMHCookie = xcb_ewmh_init_atoms( WindowGlobal::connection, &WindowGlobal::EWMH ); if (!xcb_ewmh_init_atoms_replies( &WindowGlobal::EWMH, WindowGlobal::EWMHCookie, nullptr )) { std::cout << "Fullscreen not supported." << std::endl; } xcb_ewmh_request_change_wm_state( &WindowGlobal::EWMH, XDefaultScreen( display ), WindowGlobal::window, XCB_EWMH_WM_STATE_ADD, WindowGlobal::EWMH._NET_WM_STATE_FULLSCREEN, 0, XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL ); xcb_ewmh_request_change_active_window( &WindowGlobal::EWMH, XDefaultScreen( display ), WindowGlobal::window, XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL, XCB_CURRENT_TIME, XCB_WINDOW_NONE ); xcb_generic_error_t* error; xcb_get_window_attributes_reply_t* reply = xcb_get_window_attributes_reply( WindowGlobal::connection, xcb_get_window_attributes( WindowGlobal::connection, WindowGlobal::window ), &error ); if (!reply) { std::cerr << "Full screen reply failed" << std::endl; } } //xcb_change_property( WindowGlobal::connection, XCB_PROP_MODE_REPLACE, WindowGlobal::window, WindowGlobal::EWMH._NET_WM_STATE, XCB_ATOM, 32, 1, &(WindowGlobal::EWMH._NET_WM_STATE_FULLSCREEN)); // End test GLXWindow glxwindow = glXCreateWindow( display, fb_config, WindowGlobal::window, nullptr ); if (!glxwindow) { xcb_destroy_window( connection, WindowGlobal::window ); glXDestroyContext( display, context ); std::cerr << "glXDestroyContext failed" << std::endl; return -1; } WindowGlobal::drawable = glxwindow; if (!glXMakeContextCurrent( display, WindowGlobal::drawable, WindowGlobal::drawable, context )) { xcb_destroy_window( connection, WindowGlobal::window ); glXDestroyContext( display, context ); std::cerr << "glXMakeContextCurrent failed" << std::endl; return -1; } return 0; }
/* FIXME: round trips */ static Shadow * _ecore_x_window_tree_walk(Ecore_X_Window window) { Shadow *s, **sl; xcb_get_window_attributes_reply_t *reply_attr; xcb_get_geometry_reply_t *reply_geom; xcb_query_tree_reply_t *reply_tree; xcb_get_window_attributes_cookie_t cookie_attr; xcb_get_geometry_cookie_t cookie_geom; xcb_query_tree_cookie_t cookie_tree; int i, j; CHECK_XCB_CONN; cookie_attr = xcb_get_window_attributes_unchecked(_ecore_xcb_conn, window); reply_attr = xcb_get_window_attributes_reply(_ecore_xcb_conn, cookie_attr, NULL); if (!reply_attr) return NULL; if (reply_attr->map_state != XCB_MAP_STATE_VIEWABLE) { free(reply_attr); return NULL; } free(reply_attr); cookie_geom = xcb_get_geometry_unchecked(_ecore_xcb_conn, window); reply_geom = xcb_get_geometry_reply(_ecore_xcb_conn, cookie_geom, NULL); if (!reply_geom) return NULL; if (!(s = calloc(1, sizeof(Shadow)))) { free(reply_geom); return NULL; } s->win = window; s->x = reply_geom->x; s->y = reply_geom->y; s->w = reply_geom->width; s->h = reply_geom->height; free(reply_geom); cookie_tree = xcb_query_tree_unchecked(_ecore_xcb_conn, window); reply_tree = xcb_query_tree_reply(_ecore_xcb_conn, cookie_tree, NULL); if (reply_tree) { xcb_window_t *list; int num; num = xcb_query_tree_children_length(reply_tree); list = xcb_query_tree_children(reply_tree); s->children = calloc(1, sizeof(Shadow *) * num); if (s->children) { s->children_num = num; for (i = 0; i < num; i++) { s->children[i] = _ecore_x_window_tree_walk(list[i]); if (s->children[i]) s->children[i]->parent = s; } /* compress list down */ j = 0; for (i = 0; i < num; i++) { if (s->children[i]) { s->children[j] = s->children[i]; j++; } } if (j == 0) { free(s->children); s->children = NULL; s->children_num = 0; } else { s->children_num = j; sl = realloc(s->children, sizeof(Shadow *) * j); if (sl) s->children = sl; } } free(reply_tree); } return s; }
/* * Do some sanity checks and then reparent the window. * */ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie, bool needs_to_be_mapped) { xcb_drawable_t d = {window}; xcb_get_geometry_cookie_t geomc; xcb_get_geometry_reply_t *geom; xcb_get_window_attributes_reply_t *attr = NULL; xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie, utf8_title_cookie, title_cookie, class_cookie, leader_cookie, transient_cookie, role_cookie, startup_id_cookie, wm_hints_cookie, wm_normal_hints_cookie, motif_wm_hints_cookie; geomc = xcb_get_geometry(conn, d); /* Check if the window is mapped (it could be not mapped when intializing and calling manage_window() for every window) */ if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) { DLOG("Could not get attributes\n"); xcb_discard_reply(conn, geomc.sequence); return; } if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) { xcb_discard_reply(conn, geomc.sequence); goto out; } /* Don’t manage clients with the override_redirect flag */ if (attr->override_redirect) { xcb_discard_reply(conn, geomc.sequence); goto out; } /* Check if the window is already managed */ if (con_by_window_id(window) != NULL) { DLOG("already managed (by con %p)\n", con_by_window_id(window)); xcb_discard_reply(conn, geomc.sequence); goto out; } /* Get the initial geometry (position, size, …) */ if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) { DLOG("could not get geometry\n"); goto out; } uint32_t values[1]; /* Set a temporary event mask for the new window, consisting only of * PropertyChange and StructureNotify. We need to be notified of * PropertyChanges because the client can change its properties *after* we * requested them but *before* we actually reparented it and have set our * final event mask. * We need StructureNotify because the client may unmap the window before * we get to re-parent it. * If this request fails, we assume the client has already unmapped the * window between the MapRequest and our event mask change. */ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_void_cookie_t event_mask_cookie = xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values); if (xcb_request_check(conn, event_mask_cookie) != NULL) { LOG("Could not change event mask, the window probably already disappeared.\n"); goto out; } #define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len) wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX); strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX); state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX); utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128); leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX); transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX); title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128); class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128); role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128); startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512); wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window); wm_normal_hints_cookie = xcb_icccm_get_wm_normal_hints(conn, window); motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t)); DLOG("Managing window 0x%08x\n", window); i3Window *cwindow = scalloc(1, sizeof(i3Window)); cwindow->id = window; cwindow->depth = get_visual_depth(attr->visual); /* We need to grab buttons 1-3 for click-to-focus and buttons 1-5 * to allow for mouse bindings using --whole-window to work correctly. */ xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, XCB_BUTTON_INDEX_ANY, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); /* update as much information as possible so far (some replies may be NULL) */ window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true); window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true); window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL)); window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL)); window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL)); window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true); bool urgency_hint; window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint); border_style_t motif_border_style = BS_NORMAL; window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style); xcb_size_hints_t wm_size_hints; if (!xcb_icccm_get_wm_size_hints_reply(conn, wm_normal_hints_cookie, &wm_size_hints, NULL)) memset(&wm_size_hints, '\0', sizeof(xcb_size_hints_t)); xcb_get_property_reply_t *type_reply = xcb_get_property_reply(conn, wm_type_cookie, NULL); xcb_get_property_reply_t *state_reply = xcb_get_property_reply(conn, state_cookie, NULL); xcb_get_property_reply_t *startup_id_reply; startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL); char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply); DLOG("startup workspace = %s\n", startup_ws); /* check if the window needs WM_TAKE_FOCUS */ cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS); /* read the preferred _NET_WM_WINDOW_TYPE atom */ cwindow->window_type = xcb_get_preferred_window_type(type_reply); /* Where to start searching for a container that swallows the new one? */ Con *search_at = croot; if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DOCK)) { LOG("This window is of type dock\n"); Output *output = get_output_containing(geom->x, geom->y); if (output != NULL) { DLOG("Starting search at output %s\n", output->name); search_at = output->con; } /* find out the desired position of this dock window */ if (cwindow->reserved.top > 0 && cwindow->reserved.bottom == 0) { DLOG("Top dock client\n"); cwindow->dock = W_DOCK_TOP; } else if (cwindow->reserved.top == 0 && cwindow->reserved.bottom > 0) { DLOG("Bottom dock client\n"); cwindow->dock = W_DOCK_BOTTOM; } else { DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n"); if (geom->y < (int16_t)(search_at->rect.height / 2)) { DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n", geom->y, (search_at->rect.height / 2)); cwindow->dock = W_DOCK_TOP; } else { DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n", geom->y, (search_at->rect.height / 2)); cwindow->dock = W_DOCK_BOTTOM; } } } DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height); Con *nc = NULL; Match *match = NULL; Assignment *assignment; /* TODO: two matches for one container */ /* See if any container swallows this new window */ nc = con_for_window(search_at, cwindow, &match); if (nc == NULL) { /* If not, check if it is assigned to a specific workspace */ if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE))) { DLOG("Assignment matches (%p)\n", match); Con *assigned_ws = workspace_get(assignment->dest.workspace, NULL); nc = con_descend_tiling_focused(assigned_ws); DLOG("focused on ws %s: %p / %s\n", assigned_ws->name, nc, nc->name); if (nc->type == CT_WORKSPACE) nc = tree_open_con(nc, cwindow); else nc = tree_open_con(nc->parent, cwindow); /* set the urgency hint on the window if the workspace is not visible */ if (!workspace_is_visible(assigned_ws)) urgency_hint = true; } else if (startup_ws) { /* If it’s not assigned, but was started on a specific workspace, * we want to open it there */ DLOG("Using workspace on which this application was started (%s)\n", startup_ws); nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL)); DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name); if (nc->type == CT_WORKSPACE) nc = tree_open_con(nc, cwindow); else nc = tree_open_con(nc->parent, cwindow); } else { /* If not, insert it at the currently focused position */ if (focused->type == CT_CON && con_accepts_window(focused)) { LOG("using current container, focused = %p, focused->name = %s\n", focused, focused->name); nc = focused; } else nc = tree_open_con(NULL, cwindow); } } else { /* M_BELOW inserts the new window as a child of the one which was * matched (e.g. dock areas) */ if (match != NULL && match->insert_where == M_BELOW) { nc = tree_open_con(nc, cwindow); } /* If M_BELOW is not used, the container is replaced. This happens with * "swallows" criteria that are used for stored layouts, in which case * we need to remove that criterion, because they should only be valid * once. */ if (match != NULL && match->insert_where != M_BELOW) { DLOG("Removing match %p from container %p\n", match, nc); TAILQ_REMOVE(&(nc->swallow_head), match, matches); match_free(match); } } DLOG("new container = %p\n", nc); if (nc->window != NULL && nc->window != cwindow) { if (!restore_kill_placeholder(nc->window->id)) { DLOG("Uh?! Container without a placeholder, but with a window, has swallowed this to-be-managed window?!\n"); } else { /* Remove remaining criteria, the first swallowed window wins. */ while (!TAILQ_EMPTY(&(nc->swallow_head))) { Match *first = TAILQ_FIRST(&(nc->swallow_head)); TAILQ_REMOVE(&(nc->swallow_head), first, matches); match_free(first); } } } nc->window = cwindow; x_reinit(nc); nc->border_width = geom->border_width; char *name; sasprintf(&name, "[i3 con] container around %p", cwindow); x_set_name(nc, name); free(name); /* handle fullscreen containers */ Con *ws = con_get_workspace(nc); Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL); if (fs == NULL) fs = con_get_fullscreen_con(croot, CF_GLOBAL); if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) { /* If this window is already fullscreen (after restarting!), skip * toggling fullscreen, that would drop it out of fullscreen mode. */ if (fs != nc) con_toggle_fullscreen(nc, CF_OUTPUT); fs = NULL; } bool set_focus = false; if (fs == NULL) { DLOG("Not in fullscreen mode, focusing\n"); if (!cwindow->dock) { /* Check that the workspace is visible and on the same output as * the current focused container. If the window was assigned to an * invisible workspace, we should not steal focus. */ Con *current_output = con_get_output(focused); Con *target_output = con_get_output(ws); if (workspace_is_visible(ws) && current_output == target_output) { if (!match || !match->restart_mode) { set_focus = true; } else DLOG("not focusing, matched with restart_mode == true\n"); } else DLOG("workspace not visible, not focusing\n"); } else DLOG("dock, not focusing\n"); } else { DLOG("fs = %p, ws = %p, not focusing\n", fs, ws); /* Insert the new container in focus stack *after* the currently * focused (fullscreen) con. This way, the new container will be * focused after we return from fullscreen mode */ Con *first = TAILQ_FIRST(&(nc->parent->focus_head)); if (first != nc) { /* We only modify the focus stack if the container is not already * the first one. This can happen when existing containers swallow * new windows, for example when restarting. */ TAILQ_REMOVE(&(nc->parent->focus_head), nc, focused); TAILQ_INSERT_AFTER(&(nc->parent->focus_head), first, nc, focused); } } /* set floating if necessary */ bool want_floating = false; if (xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_DIALOG) || xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_UTILITY) || xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) || xcb_reply_contains_atom(type_reply, A__NET_WM_WINDOW_TYPE_SPLASH) || xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_MODAL) || (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE && wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE && wm_size_hints.min_height == wm_size_hints.max_height && wm_size_hints.min_width == wm_size_hints.max_width)) { LOG("This window is a dialog window, setting floating\n"); want_floating = true; } if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_STICKY)) nc->sticky = true; FREE(state_reply); FREE(type_reply); if (cwindow->transient_for != XCB_NONE || (cwindow->leader != XCB_NONE && cwindow->leader != cwindow->id && con_by_window_id(cwindow->leader) != NULL)) { LOG("This window is transient for another window, setting floating\n"); want_floating = true; if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && fs != NULL) { LOG("There is a fullscreen window, leaving fullscreen mode\n"); con_toggle_fullscreen(fs, CF_OUTPUT); } else if (config.popup_during_fullscreen == PDF_SMART && fs != NULL && fs->window != NULL) { i3Window *transient_win = cwindow; while (transient_win != NULL && transient_win->transient_for != XCB_NONE) { if (transient_win->transient_for == fs->window->id) { LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n"); set_focus = true; break; } Con *next_transient = con_by_window_id(transient_win->transient_for); if (next_transient == NULL) break; /* Some clients (e.g. x11-ssh-askpass) actually set * WM_TRANSIENT_FOR to their own window id, so break instead of * looping endlessly. */ if (transient_win == next_transient->window) break; transient_win = next_transient->window; } } } /* dock clients cannot be floating, that makes no sense */ if (cwindow->dock) want_floating = false; /* Plasma windows set their geometry in WM_SIZE_HINTS. */ if ((wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_POSITION) && (wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_US_SIZE || wm_size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)) { DLOG("We are setting geometry according to wm_size_hints x=%d y=%d w=%d h=%d\n", wm_size_hints.x, wm_size_hints.y, wm_size_hints.width, wm_size_hints.height); geom->x = wm_size_hints.x; geom->y = wm_size_hints.y; geom->width = wm_size_hints.width; geom->height = wm_size_hints.height; } /* Store the requested geometry. The width/height gets raised to at least * 75x50 when entering floating mode, which is the minimum size for a * window to be useful (smaller windows are usually overlays/toolbars/… * which are not managed by the wm anyways). We store the original geometry * here because it’s used for dock clients. */ if (nc->geometry.width == 0) nc->geometry = (Rect){geom->x, geom->y, geom->width, geom->height}; if (motif_border_style != BS_NORMAL) { DLOG("MOTIF_WM_HINTS specifies decorations (border_style = %d)\n", motif_border_style); if (want_floating) { con_set_border_style(nc, motif_border_style, config.default_floating_border_width); } else { con_set_border_style(nc, motif_border_style, config.default_border_width); } } if (want_floating) { DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height); /* automatically set the border to the default value if a motif border * was not specified */ bool automatic_border = (motif_border_style == BS_NORMAL); floating_enable(nc, automatic_border); } /* explicitly set the border width to the default */ if (nc->current_border_width == -1) { nc->current_border_width = (want_floating ? config.default_floating_border_width : config.default_border_width); } /* to avoid getting an UnmapNotify event due to reparenting, we temporarily * declare no interest in any state change event of this window */ values[0] = XCB_NONE; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0); if (xcb_request_check(conn, rcookie) != NULL) { LOG("Could not reparent the window, aborting\n"); goto geom_out; } values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_flush(conn); /* Put the client inside the save set. Upon termination (whether killed or * normal exit does not matter) of the window manager, these clients will * be correctly reparented to their most closest living ancestor (= * cleanup) */ xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window); /* Check if any assignments match */ run_assignments(cwindow); /* 'ws' may be invalid because of the assignments, e.g. when the user uses * "move window to workspace 1", but had it assigned to workspace 2. */ ws = con_get_workspace(nc); /* If this window was put onto an invisible workspace (via assignments), we * render this workspace. It wouldn’t be rendered in our normal code path * because only the visible workspaces get rendered. * * By rendering the workspace, we assign proper coordinates (read: not * width=0, height=0) to the window, which is important for windows who * actually use them to position their GUI elements, e.g. rhythmbox. */ if (ws && !workspace_is_visible(ws)) { /* This is a bit hackish: we need to copy the content container’s rect * to the workspace, because calling render_con() on the content * container would also take the shortcut and not render the invisible * workspace at all. However, just calling render_con() on the * workspace isn’t enough either — it needs the rect. */ ws->rect = ws->parent->rect; render_con(ws, true); /* Disable setting focus, otherwise we’d move focus to an invisible * workspace, which we generally prevent (e.g. in * con_move_to_workspace). */ set_focus = false; } render_con(croot, false); /* Send an event about window creation */ ipc_send_window_event("new", nc); if (set_focus && assignment_for(cwindow, A_NO_FOCUS) != NULL) { /* The first window on a workspace should always be focused. We have to * compare with == 1 because the container has already been inserted at * this point. */ if (con_num_children(ws) == 1) { DLOG("This is the first window on this workspace, ignoring no_focus.\n"); } else { DLOG("no_focus was set for con = %p, not setting focus.\n", nc); set_focus = false; } } /* Defer setting focus after the 'new' event has been sent to ensure the * proper window event sequence. */ if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { DLOG("Now setting focus.\n"); con_focus(nc); } tree_render(); /* Windows might get managed with the urgency hint already set (Pidgin is * known to do that), so check for that and handle the hint accordingly. * This code needs to be in this part of manage_window() because the window * needs to be on the final workspace first. */ con_set_urgency(nc, urgency_hint); geom_out: free(geom); out: free(attr); return; }
/* * Do some sanity checks and then reparent the window. * */ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cookie, bool needs_to_be_mapped) { xcb_drawable_t d = { window }; xcb_get_geometry_cookie_t geomc; xcb_get_geometry_reply_t *geom; xcb_get_window_attributes_reply_t *attr = NULL; xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie, utf8_title_cookie, title_cookie, class_cookie, leader_cookie, transient_cookie, role_cookie, startup_id_cookie, wm_hints_cookie; #ifdef USE_ICONS xcb_get_property_cookie_t wm_icon_cookie; #endif geomc = xcb_get_geometry(conn, d); #define FREE_GEOMETRY() do { \ if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) != NULL) \ free(geom); \ } while (0) /* Check if the window is mapped (it could be not mapped when intializing and calling manage_window() for every window) */ if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) { DLOG("Could not get attributes\n"); FREE_GEOMETRY(); return; } if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) { FREE_GEOMETRY(); goto out; } /* Don’t manage clients with the override_redirect flag */ if (attr->override_redirect) { FREE_GEOMETRY(); goto out; } /* Check if the window is already managed */ if (con_by_window_id(window) != NULL) { DLOG("already managed (by con %p)\n", con_by_window_id(window)); FREE_GEOMETRY(); goto out; } /* Get the initial geometry (position, size, …) */ if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL) { DLOG("could not get geometry\n"); goto out; } uint32_t values[1]; /* Set a temporary event mask for the new window, consisting only of * PropertyChange and StructureNotify. We need to be notified of * PropertyChanges because the client can change its properties *after* we * requested them but *before* we actually reparented it and have set our * final event mask. * We need StructureNotify because the client may unmap the window before * we get to re-parent it. * If this request fails, we assume the client has already unmapped the * window between the MapRequest and our event mask change. */ values[0] = XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY; xcb_void_cookie_t event_mask_cookie = xcb_change_window_attributes_checked(conn, window, XCB_CW_EVENT_MASK, values); if (xcb_request_check(conn, event_mask_cookie) != NULL) { LOG("Could not change event mask, the window probably already disappeared.\n"); goto out; } #define GET_PROPERTY(atom, len) xcb_get_property(conn, false, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, len) wm_type_cookie = GET_PROPERTY(A__NET_WM_WINDOW_TYPE, UINT32_MAX); strut_cookie = GET_PROPERTY(A__NET_WM_STRUT_PARTIAL, UINT32_MAX); state_cookie = GET_PROPERTY(A__NET_WM_STATE, UINT32_MAX); utf8_title_cookie = GET_PROPERTY(A__NET_WM_NAME, 128); leader_cookie = GET_PROPERTY(A_WM_CLIENT_LEADER, UINT32_MAX); transient_cookie = GET_PROPERTY(XCB_ATOM_WM_TRANSIENT_FOR, UINT32_MAX); title_cookie = GET_PROPERTY(XCB_ATOM_WM_NAME, 128); class_cookie = GET_PROPERTY(XCB_ATOM_WM_CLASS, 128); role_cookie = GET_PROPERTY(A_WM_WINDOW_ROLE, 128); startup_id_cookie = GET_PROPERTY(A__NET_STARTUP_ID, 512); wm_hints_cookie = xcb_icccm_get_wm_hints(conn, window); #ifdef USE_ICONS wm_icon_cookie = xcb_get_property_unchecked(conn, false, window, A__NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX); #endif /* TODO: also get wm_normal_hints here. implement after we got rid of xcb-event */ DLOG("Managing window 0x%08x\n", window); i3Window *cwindow = scalloc(sizeof(i3Window)); cwindow->id = window; cwindow->depth = get_visual_depth(attr->visual); /* We need to grab the mouse buttons for click to focus */ xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 1 /* left mouse button */, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 2 /* middle mouse button */, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); xcb_grab_button(conn, false, window, XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE, 3 /* right mouse button */, XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */); /* update as much information as possible so far (some replies may be NULL) */ window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true); window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true); window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL)); window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL)); window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL)); window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true); window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL)); #ifdef USE_ICONS window_update_icon(cwindow, xcb_get_property_reply(conn, wm_icon_cookie, NULL)); #endif xcb_get_property_reply_t *startup_id_reply; startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL); char *startup_ws = startup_workspace_for_window(cwindow, startup_id_reply); DLOG("startup workspace = %s\n", startup_ws); /* check if the window needs WM_TAKE_FOCUS */ cwindow->needs_take_focus = window_supports_protocol(cwindow->id, A_WM_TAKE_FOCUS); /* Where to start searching for a container that swallows the new one? */ Con *search_at = croot; xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, wm_type_cookie, NULL); if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DOCK)) { LOG("This window is of type dock\n"); Output *output = get_output_containing(geom->x, geom->y); if (output != NULL) { DLOG("Starting search at output %s\n", output->name); search_at = output->con; } /* find out the desired position of this dock window */ if (cwindow->reserved.top > 0 && cwindow->reserved.bottom == 0) { DLOG("Top dock client\n"); cwindow->dock = W_DOCK_TOP; } else if (cwindow->reserved.top == 0 && cwindow->reserved.bottom > 0) { DLOG("Bottom dock client\n"); cwindow->dock = W_DOCK_BOTTOM; } else { DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n"); if (geom->y < (search_at->rect.height / 2)) { DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n", geom->y, (search_at->rect.height / 2)); cwindow->dock = W_DOCK_TOP; } else { DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n", geom->y, (search_at->rect.height / 2)); cwindow->dock = W_DOCK_BOTTOM; } } } DLOG("Initial geometry: (%d, %d, %d, %d)\n", geom->x, geom->y, geom->width, geom->height); Con *nc = NULL; Match *match = NULL; Assignment *assignment; /* TODO: two matches for one container */ /* See if any container swallows this new window */ nc = con_for_window(search_at, cwindow, &match); if (nc == NULL) { /* If not, check if it is assigned to a specific workspace / output */ if ((assignment = assignment_for(cwindow, A_TO_WORKSPACE | A_TO_OUTPUT))) { DLOG("Assignment matches (%p)\n", match); if (assignment->type == A_TO_WORKSPACE) { nc = con_descend_tiling_focused(workspace_get(assignment->dest.workspace, NULL)); DLOG("focused on ws %s: %p / %s\n", assignment->dest.workspace, nc, nc->name); if (nc->type == CT_WORKSPACE) nc = tree_open_con(nc, cwindow); else nc = tree_open_con(nc->parent, cwindow); } /* TODO: handle assignments with type == A_TO_OUTPUT */ } else if (startup_ws) { /* If it’s not assigned, but was started on a specific workspace, * we want to open it there */ DLOG("Using workspace on which this application was started (%s)\n", startup_ws); nc = con_descend_tiling_focused(workspace_get(startup_ws, NULL)); DLOG("focused on ws %s: %p / %s\n", startup_ws, nc, nc->name); if (nc->type == CT_WORKSPACE) nc = tree_open_con(nc, cwindow); else nc = tree_open_con(nc->parent, cwindow); } else { /* If not, insert it at the currently focused position */ if (focused->type == CT_CON && con_accepts_window(focused)) { LOG("using current container, focused = %p, focused->name = %s\n", focused, focused->name); nc = focused; } else nc = tree_open_con(NULL, cwindow); } } else { /* M_BELOW inserts the new window as a child of the one which was * matched (e.g. dock areas) */ if (match != NULL && match->insert_where == M_BELOW) { nc = tree_open_con(nc, cwindow); } } DLOG("new container = %p\n", nc); nc->window = cwindow; x_reinit(nc); nc->border_width = geom->border_width; char *name; sasprintf(&name, "[i3 con] container around %p", cwindow); x_set_name(nc, name); free(name); Con *ws = con_get_workspace(nc); Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL); if (fs == NULL) fs = con_get_fullscreen_con(croot, CF_GLOBAL); if (fs == NULL) { DLOG("Not in fullscreen mode, focusing\n"); if (!cwindow->dock) { /* Check that the workspace is visible and on the same output as * the current focused container. If the window was assigned to an * invisible workspace, we should not steal focus. */ Con *current_output = con_get_output(focused); Con *target_output = con_get_output(ws); if (workspace_is_visible(ws) && current_output == target_output) { if (!match || !match->restart_mode) { con_focus(nc); } else DLOG("not focusing, matched with restart_mode == true\n"); } else DLOG("workspace not visible, not focusing\n"); } else DLOG("dock, not focusing\n"); } else { DLOG("fs = %p, ws = %p, not focusing\n", fs, ws); /* Insert the new container in focus stack *after* the currently * focused (fullscreen) con. This way, the new container will be * focused after we return from fullscreen mode */ Con *first = TAILQ_FIRST(&(nc->parent->focus_head)); if (first != nc) { /* We only modify the focus stack if the container is not already * the first one. This can happen when existing containers swallow * new windows, for example when restarting. */ TAILQ_REMOVE(&(nc->parent->focus_head), nc, focused); TAILQ_INSERT_AFTER(&(nc->parent->focus_head), first, nc, focused); } } /* set floating if necessary */ bool want_floating = false; if (xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_DIALOG) || xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_UTILITY) || xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_TOOLBAR) || xcb_reply_contains_atom(reply, A__NET_WM_WINDOW_TYPE_SPLASH)) { LOG("This window is a dialog window, setting floating\n"); want_floating = true; } FREE(reply); if (cwindow->transient_for != XCB_NONE || (cwindow->leader != XCB_NONE && cwindow->leader != cwindow->id && con_by_window_id(cwindow->leader) != NULL)) { LOG("This window is transient for another window, setting floating\n"); want_floating = true; if (config.popup_during_fullscreen == PDF_LEAVE_FULLSCREEN && fs != NULL) { LOG("There is a fullscreen window, leaving fullscreen mode\n"); con_toggle_fullscreen(fs, CF_OUTPUT); } else if (config.popup_during_fullscreen == PDF_SMART && fs != NULL && fs->window != NULL) { i3Window *transient_win = cwindow; while (transient_win != NULL && transient_win->transient_for != XCB_NONE) { if (transient_win->transient_for == fs->window->id) { LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n"); con_focus(nc); break; } Con *next_transient = con_by_window_id(transient_win->transient_for); if (next_transient == NULL) break; transient_win = next_transient->window; } } } /* dock clients cannot be floating, that makes no sense */ if (cwindow->dock) want_floating = false; /* Store the requested geometry. The width/height gets raised to at least * 75x50 when entering floating mode, which is the minimum size for a * window to be useful (smaller windows are usually overlays/toolbars/… * which are not managed by the wm anyways). We store the original geometry * here because it’s used for dock clients. */ nc->geometry = (Rect){ geom->x, geom->y, geom->width, geom->height }; if (want_floating) { DLOG("geometry = %d x %d\n", nc->geometry.width, nc->geometry.height); floating_enable(nc, true); } /* to avoid getting an UnmapNotify event due to reparenting, we temporarily * declare no interest in any state change event of this window */ values[0] = XCB_NONE; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_void_cookie_t rcookie = xcb_reparent_window_checked(conn, window, nc->frame, 0, 0); if (xcb_request_check(conn, rcookie) != NULL) { LOG("Could not reparent the window, aborting\n"); goto geom_out; } values[0] = CHILD_EVENT_MASK & ~XCB_EVENT_MASK_ENTER_WINDOW; xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values); xcb_flush(conn); reply = xcb_get_property_reply(conn, state_cookie, NULL); if (xcb_reply_contains_atom(reply, A__NET_WM_STATE_FULLSCREEN)) con_toggle_fullscreen(nc, CF_OUTPUT); FREE(reply); /* Put the client inside the save set. Upon termination (whether killed or * normal exit does not matter) of the window manager, these clients will * be correctly reparented to their most closest living ancestor (= * cleanup) */ xcb_change_save_set(conn, XCB_SET_MODE_INSERT, window); /* Check if any assignments match */ run_assignments(cwindow); /* 'ws' may be invalid because of the assignments, e.g. when the user uses * "move window to workspace 1", but had it assigned to workspace 2. */ ws = con_get_workspace(nc); /* If this window was put onto an invisible workspace (via assignments), we * render this workspace. It wouldn’t be rendered in our normal code path * because only the visible workspaces get rendered. * * By rendering the workspace, we assign proper coordinates (read: not * width=0, height=0) to the window, which is important for windows who * actually use them to position their GUI elements, e.g. rhythmbox. */ if (ws && !workspace_is_visible(ws)) { /* This is a bit hackish: we need to copy the content container’s rect * to the workspace, because calling render_con() on the content * container would also take the shortcut and not render the invisible * workspace at all. However, just calling render_con() on the * workspace isn’t enough either — it needs the rect. */ ws->rect = ws->parent->rect; render_con(ws, true); } tree_render(); /* Send an event about window creation */ ipc_send_window_new_event(nc); geom_out: free(geom); out: free(attr); return; }
int main(int argc,char**argv){ xcb_connection_t*d=xcb_connect(0,0); int32_t*x,*y,*tx=0,mx,my,rt=xcb_setup_roots_iterator(xcb_get_setup(d)).data->root,cs[255],*cz=cs+1; uint8_t mz,mZ; xcb_change_window_attributes(d,rt,XCB_CW_EVENT_MASK,&cwa); xcb_grab_key(d,1,rt,0,64,XCB_GRAB_MODE_ASYNC,XCB_GRAB_MODE_ASYNC); xcb_grab_key(d,1,rt,8,XCB_GRAB_ANY,XCB_GRAB_MODE_ASYNC,XCB_GRAB_MODE_ASYNC); xcb_grab_button(d,1,rt,XCB_EVENT_MASK_BUTTON_PRESS,XCB_GRAB_MODE_ASYNC,XCB_GRAB_MODE_ASYNC,XCB_NONE,XCB_NONE,XCB_GRAB_ANY,8); #ifdef COMPOSITE xcb_composite_redirect_subwindows(d,rt,XCB_COMPOSITE_REDIRECT_AUTOMATIC); #endif xcb_generic_event_t*e=0; main:xcb_flush(d); waitpid(-1,0,WNOHANG); noflush:x=y=cz-1; again:free(e); switch((e=xcb_wait_for_event(d))->response_type&127){ case XCB_BUTTON_PRESS: for(;x>cs;x--) if(*x==((xcb_button_press_event_t*)e)->child){ if(((xcb_key_press_event_t*)e)->detail==2)goto pocus; case XCB_KEY_PRESS: mz=128|((xcb_key_press_event_t*)e)->detail; my=((xcb_key_press_event_t*)e)->state; goto*(cz==cs+1?&&kcode:&&stack); } goto noflush; case XCB_KEY_RELEASE: if(((xcb_key_press_event_t*)e)->detail!=64||!tx)default:goto again; xt:x=tx; tx=0; goto stack; case XCB_CONFIGURE_REQUEST:{ void*p=buf; for(mz=0;mz<5;mz++) if(((xcb_configure_request_event_t*)e)->value_mask&1<<mz){*(uint32_t*)p=*(int16_t*)(((void*)e)+16+mz*2);p+=4;} if(((xcb_configure_request_event_t*)e)->value_mask&XCB_CONFIG_WINDOW_SIBLING){*(uint32_t*)p=((xcb_configure_request_event_t*)e)->sibling;p+=4;} if(mz=((xcb_configure_request_event_t*)e)->value_mask&XCB_CONFIG_WINDOW_STACK_MODE)*(uint32_t*)p=((xcb_configure_request_event_t*)e)->stack_mode; xcb_configure_window(d,((xcb_configure_request_event_t*)e)->window,((xcb_configure_request_event_t*)e)->value_mask,buf); if(mz){ p=xcb_query_tree_reply(d,xcb_query_tree_unchecked(d,rt),0); int32_t*cl=p+32+((xcb_query_tree_reply_t*)p)->children_len*4; for(y=p+32;y<cl;y++){ for(x=cs+1;x<cz;x++) if(*x==*y)goto nono; *y=0; nono:; } x=cs; for(y=p+32;y<cl;y++) if(*y)*++x=*y; free(p); goto pocus; }else goto main;} case XCB_MAP_REQUEST:{ void*p=xcb_get_window_attributes_reply(d,xcb_get_window_attributes_unchecked(d,((xcb_map_request_event_t*)e)->window),0); if(((xcb_get_window_attributes_reply_t*)p)->override_redirect){ free(p); goto pocus; } free(p); for(;x>cs;x--) if(*x==((xcb_map_request_event_t*)e)->window)goto noflush; xcb_map_window(d,*cz++=((xcb_map_request_event_t*)e)->window); goto hocus;} case XCB_MOTION_NOTIFY: *buf=mZ&&((xcb_motion_notify_event_t*)e)->root_x<=mx?:((xcb_motion_notify_event_t*)e)->root_x-mx; buf[1]=mZ&&((xcb_motion_notify_event_t*)e)->root_y<=my?:((xcb_motion_notify_event_t*)e)->root_y-my; xcb_configure_window(d,*x,mZ?XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT:XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y,buf); goto main; case XCB_BUTTON_RELEASE: xcb_ungrab_pointer(d,XCB_CURRENT_TIME); goto main; case XCB_UNMAP_NOTIFY:unmap:goto*(x==cs?&&noflush:*x==((xcb_unmap_notify_event_t*)e)->window&&--cz>cs+1?&&stack:(x--,&&unmap)); } stack:mx=*x; for(;x!=y;x+=x<y?:-1)*x=x[x<y?:-1]; *x=mx; hocus:x=cz-1; xcb_configure_window(d,*x,XCB_CONFIG_WINDOW_STACK_MODE,di); pocus:xcb_set_input_focus(d,XCB_INPUT_FOCUS_POINTER_ROOT,*x,XCB_CURRENT_TIME); if(!(mz&128))goto main; kcode:switch(mz&=127){ void*p; case 1:case 3: p=xcb_grab_pointer_reply(d,xcb_grab_pointer_unchecked(d,0,rt,XCB_EVENT_MASK_BUTTON_RELEASE|XCB_EVENT_MASK_POINTER_MOTION,XCB_GRAB_MODE_ASYNC,XCB_GRAB_MODE_ASYNC,XCB_NONE,XCB_NONE,XCB_CURRENT_TIME),0); if(((xcb_grab_pointer_reply_t*)p)->status!=XCB_GRAB_STATUS_SUCCESS){ free(p); goto noflush; } free(p); p=xcb_get_geometry_reply(d,xcb_get_geometry_unchecked(d,*y),0); mx=((xcb_get_geometry_reply_t*)p)->x; my=((xcb_get_geometry_reply_t*)p)->y; free(p); if(mZ=mz==1){ p=xcb_query_pointer_reply(d,xcb_query_pointer_unchecked(d,rt),0); mx=((xcb_query_pointer_reply_t*)p)->root_x-mx; my=((xcb_query_pointer_reply_t*)p)->root_y-my; free(p); } goto noflush; case 23:case 49: if(cz-cs<3)goto main; y=tx; tx=mz==23?(y!=cs+1?(y?:x)-1:x):!y||y==x?cs+1:y+1; if(y&&y<cz-1){ *buf=y[mz==23?:-1]; buf[1]=mz==23; xcb_configure_window(d,*y,XCB_CONFIG_WINDOW_SIBLING|XCB_CONFIG_WINDOW_STACK_MODE,buf); } xcb_configure_window(d,*tx,XCB_CONFIG_WINDOW_STACK_MODE,di); goto main; case 32:return 0; case 44: if(cz>cs+1)xcb_configure_window(d,*y,XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,di); goto main; case 46: if(cz==cs+1)goto main; if(tx)goto*(mz|=128,&&xt); {xcb_intern_atom_cookie_t c1=xcb_intern_atom_unchecked(d,0,12,"WM_PROTOCOLS"),c2=xcb_intern_atom_unchecked(d,0,16,"WM_DELETE_WINDOW"); p=xcb_intern_atom_reply(d,c1,0); mx=((xcb_intern_atom_reply_t*)p)->atom; free(p); p=xcb_intern_atom_reply(d,c2,0);} my=((xcb_intern_atom_reply_t*)p)->atom; free(p); p=xcb_get_property_reply(d,xcb_get_property_unchecked(d,0,*y,mx,XCB_ATOM_ATOM,0,-1),0); xcb_send_event(d,0,*y,XCB_EVENT_MASK_NO_EVENT,(void*)(xcb_client_message_event_t[]){{.response_type=XCB_CLIENT_MESSAGE,.window=*y,.type=mx,.format=32,.data.data32={my,XCB_CURRENT_TIME}}});
QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) const { if (width == 0 || height == 0) return QPixmap(); // TODO: handle multiple screens QXcbScreen *screen = const_cast<QXcbScreen *>(this); xcb_window_t root = screen->root(); if (window == 0) window = root; xcb_get_geometry_cookie_t geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), window); xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL); if (!reply) { return QPixmap(); } if (width < 0) width = reply->width - x; if (height < 0) height = reply->height - y; geometry_cookie = xcb_get_geometry_unchecked(xcb_connection(), root); xcb_get_geometry_reply_t *root_reply = xcb_get_geometry_reply(xcb_connection(), geometry_cookie, NULL); if (!root_reply) { free(reply); return QPixmap(); } if (reply->depth == root_reply->depth) { // if the depth of the specified window and the root window are the // same, grab pixels from the root window (so that we get the any // overlapping windows and window manager frames) // map x and y to the root window xcb_translate_coordinates_cookie_t translate_cookie = xcb_translate_coordinates_unchecked(xcb_connection(), window, root, x, y); xcb_translate_coordinates_reply_t *translate_reply = xcb_translate_coordinates_reply(xcb_connection(), translate_cookie, NULL); if (!translate_reply) { free(reply); free(root_reply); return QPixmap(); } x = translate_reply->dst_x; y = translate_reply->dst_y; window = root; free(translate_reply); free(reply); reply = root_reply; } else { free(root_reply); root_reply = 0; } xcb_get_window_attributes_reply_t *attributes_reply = xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), window), NULL); if (!attributes_reply) { free(reply); return QPixmap(); } const xcb_visualtype_t *visual = screen->visualForId(attributes_reply->visual); free(attributes_reply); xcb_pixmap_t pixmap = xcb_generate_id(xcb_connection()); xcb_create_pixmap(xcb_connection(), reply->depth, pixmap, window, width, height); uint32_t gc_value_mask = XCB_GC_SUBWINDOW_MODE; uint32_t gc_value_list[] = { XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS }; xcb_gcontext_t gc = xcb_generate_id(xcb_connection()); xcb_create_gc(xcb_connection(), gc, pixmap, gc_value_mask, gc_value_list); xcb_copy_area(xcb_connection(), window, pixmap, gc, x, y, 0, 0, width, height); QPixmap result = qt_xcb_pixmapFromXPixmap(connection(), pixmap, width, height, reply->depth, visual); free(reply); xcb_free_gc(xcb_connection(), gc); xcb_free_pixmap(xcb_connection(), pixmap); return result; }
Compositor::Compositor() : connection_(QX11Info::connection()), root_(QX11Info::appRootWindow()), damageExt_(xcb_get_extension_data(connection_, &xcb_damage_id)), initFinished_(false) { qRegisterMetaType<ClientWindow *>(); Q_ASSERT(QCoreApplication::instance()); QCoreApplication::instance()->installNativeEventFilter(this); auto ewmhCookie = xcb_ewmh_init_atoms(connection_, &ewmh_); if (!xcb_ewmh_init_atoms_replies(&ewmh_, ewmhCookie, Q_NULLPTR)) { qFatal("Cannot init EWMH"); } auto wmCmCookie = xcb_ewmh_get_wm_cm_owner_unchecked(&ewmh_, QX11Info::appScreen()); xcb_window_t wmCmOwnerWin = XCB_NONE; if (!xcb_ewmh_get_wm_cm_owner_reply(&ewmh_, wmCmCookie, &wmCmOwnerWin, Q_NULLPTR)) { qFatal("Cannot check _NET_WM_CM_Sn"); } if (wmCmOwnerWin) { qFatal("Another compositing manager is already running"); } auto attributesCookie = xcb_get_window_attributes_unchecked(connection_, root_); auto damageQueryVersionCookie = xcb_damage_query_version_unchecked(connection_, 1, 1); auto overlayWindowCookie = xcb_composite_get_overlay_window_unchecked(connection_, root_); auto attributes = xcbReply(xcb_get_window_attributes_reply(connection_, attributesCookie, Q_NULLPTR)); if (!attributes) { qFatal("Cannot get root window attributes"); } auto newEventMask = attributes->your_event_mask | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; xcb_change_window_attributes(connection_, root_, XCB_CW_EVENT_MASK, &newEventMask); auto treeCookie = xcb_query_tree_unchecked(connection_, root_); auto rootGeometryCookie = xcb_get_geometry_unchecked(connection_, root_); auto damageVersion = xcbReply(xcb_damage_query_version_reply(connection_, damageQueryVersionCookie, Q_NULLPTR)); if (!damageVersion) { qFatal("Cannot query version of Damage extension"); } auto overlayWindow = xcbReply(xcb_composite_get_overlay_window_reply(connection_, overlayWindowCookie, Q_NULLPTR)); if (!overlayWindow) { qFatal("Cannot get overlay window"); } overlayWindow_.reset(QWindow::fromWinId(overlayWindow->overlay_win)); auto region = xcb_generate_id(connection_); xcb_xfixes_create_region(connection_, region, 0, Q_NULLPTR); xcb_xfixes_set_window_shape_region(connection_, overlayWindow->overlay_win, XCB_SHAPE_SK_INPUT, 0, 0, region); xcb_xfixes_destroy_region(connection_, region); xcb_composite_redirect_subwindows(connection_, root_, XCB_COMPOSITE_REDIRECT_MANUAL); auto rootGeometry = xcbReply(xcb_get_geometry_reply(connection_, rootGeometryCookie, Q_NULLPTR)); if (!rootGeometry) { qFatal("Cannot query root window geometry"); } rootGeometry_ = QRect(rootGeometry->x, rootGeometry->y, rootGeometry->width, rootGeometry->height); auto tree = xcbReply(xcb_query_tree_reply(connection_, treeCookie, Q_NULLPTR)); if (!tree) { qFatal("Cannot query window tree"); } auto children = xcb_query_tree_children(tree.get()); for (int i = 0; i < xcb_query_tree_children_length(tree.get()); i++) { addChildWindow(children[i]); } updateActiveWindow(); initFinished_ = true; }
xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md, bool ignoreNonXdndAwareWindows) { if (w == shapedPixmapWindow()->handle()->winId()) return 0; if (md) { xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(xcb_connection(), w); xcb_get_window_attributes_reply_t *reply = xcb_get_window_attributes_reply(xcb_connection(), cookie, 0); if (!reply) return 0; if (reply->map_state != XCB_MAP_STATE_VIEWABLE) return 0; free(reply); xcb_get_geometry_cookie_t gcookie = xcb_get_geometry(xcb_connection(), w); xcb_get_geometry_reply_t *greply = xcb_get_geometry_reply(xcb_connection(), gcookie, 0); if (!greply) return 0; QRect windowRect(greply->x, greply->y, greply->width, greply->height); free(greply); if (windowRect.contains(pos)) { bool windowContainsMouse = !ignoreNonXdndAwareWindows; { xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(xcb_connection(), false, w, connection()->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0)); xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, 0); bool isAware = reply && reply->type != XCB_NONE; free(reply); if (isAware) { const QPoint relPos = pos - windowRect.topLeft(); // When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we // need to check both here so that in the case one is set and the other is not we still get the correct result. if (connection()->hasInputShape()) windowContainsMouse = windowInteractsWithPosition(xcb_connection(), relPos, w, XCB_SHAPE_SK_INPUT); if (windowContainsMouse && connection()->hasXShape()) windowContainsMouse = windowInteractsWithPosition(xcb_connection(), relPos, w, XCB_SHAPE_SK_BOUNDING); if (!connection()->hasInputShape() && !connection()->hasXShape()) windowContainsMouse = true; if (windowContainsMouse) return w; } } xcb_query_tree_cookie_t cookie = xcb_query_tree (xcb_connection(), w); xcb_query_tree_reply_t *reply = xcb_query_tree_reply(xcb_connection(), cookie, 0); if (!reply) return 0; int nc = xcb_query_tree_children_length(reply); xcb_window_t *c = xcb_query_tree_children(reply); xcb_window_t r = 0; for (uint i = nc; !r && i--;) r = findRealWindow(pos - windowRect.topLeft(), c[i], md-1, ignoreNonXdndAwareWindows); free(reply); if (r) return r; // We didn't find a client window! Just use the // innermost window. // No children! if (!windowContainsMouse) return 0; else return w; } } return 0; }
/** Scan X to find windows to manage. */ static void scan(xcb_query_tree_cookie_t tree_c[]) { int i, x_screen, tree_c_len; const int screen_max = xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); xcb_query_tree_reply_t *tree_r; xcb_window_t *wins = NULL; xcb_get_window_attributes_reply_t *attr_r; xcb_get_geometry_reply_t *geom_r; long state; for(x_screen = 0; x_screen < screen_max; x_screen++) { tree_r = xcb_query_tree_reply(globalconf.connection, tree_c[x_screen], NULL); if(!tree_r) continue; /* Get the tree of the children windows of the current root window */ if(!(wins = xcb_query_tree_children(tree_r))) fatal("cannot get tree children"); tree_c_len = xcb_query_tree_children_length(tree_r); xcb_get_window_attributes_cookie_t attr_wins[tree_c_len]; xcb_get_property_cookie_t state_wins[tree_c_len]; for(i = 0; i < tree_c_len; i++) { attr_wins[i] = xcb_get_window_attributes_unchecked(globalconf.connection, wins[i]); state_wins[i] = window_state_get_unchecked(wins[i]); } xcb_get_geometry_cookie_t *geom_wins[tree_c_len]; for(i = 0; i < tree_c_len; i++) { attr_r = xcb_get_window_attributes_reply(globalconf.connection, attr_wins[i], NULL); state = window_state_get_reply(state_wins[i]); if(!attr_r || attr_r->override_redirect || attr_r->map_state == XCB_MAP_STATE_UNMAPPED || state == XCB_ICCCM_WM_STATE_WITHDRAWN) { geom_wins[i] = NULL; p_delete(&attr_r); continue; } p_delete(&attr_r); /* Get the geometry of the current window */ geom_wins[i] = p_alloca(xcb_get_geometry_cookie_t, 1); *(geom_wins[i]) = xcb_get_geometry_unchecked(globalconf.connection, wins[i]); } for(i = 0; i < tree_c_len; i++) { if(!geom_wins[i] || !(geom_r = xcb_get_geometry_reply(globalconf.connection, *(geom_wins[i]), NULL))) continue; /* The window can be mapped, so force it to be undrawn for startup */ xcb_unmap_window(globalconf.connection, wins[i]); client_manage(wins[i], geom_r, phys_screen_getbycoord(geom_r->x, geom_r->y, geom_r->root), x_screen, true); p_delete(&geom_r); } p_delete(&tree_r); } }