//=========================================== // Lumina-DE source code // Copyright (c) 2014-2016, Ken Moore // Available under the 3-clause BSD license // See the LICENSE file for full details //=========================================== #include "LuminaX11.h" #include #include #include #include #include #include #include #include //XCB Library includes #include #include #include #include #include #include #include #include #include //XLib includes #include #define DEBUG 0 //=============================== //=============================== // XCB LIBRARY FUNCTIONS //=============================== //=============================== LXCB::LXCB(){ xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH); if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){ qDebug() << "Error with XCB atom initializations"; }else{ qDebug() << "Number of XCB screens:" << EWMH.nb_screens; } } LXCB::~LXCB(){ xcb_ewmh_connection_wipe(&EWMH); } // private function void LXCB::createWMAtoms(){ ATOMS.clear(); atoms.clear(); //List the atoms needed by some WM functions atoms << "WM_TAKE_FOCUS" << "WM_DELETE_WINDOW" << "WM_PROTOCOLS" << "WM_CHANGE_STATE"; //WM_PROTOCOLS //Create all the requests for the atoms QList reply; for(int i=0; iatom; free(reply[i]); //done with this reply }else{ //Invalid atom - could not be created atoms.removeAt(i); reply.removeAt(i); i--; } } } // === WindowList() === QList LXCB::WindowList(bool rawlist){ if(DEBUG){ qDebug() << "XCB: WindowList()" << rawlist; } QList output; //qDebug() << "Get Client list cookie"; xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_unchecked( &EWMH, 0); xcb_ewmh_get_windows_reply_t winlist; //qDebug() << "Get client list"; if( 1 == xcb_ewmh_get_client_list_reply( &EWMH, cookie, &winlist, NULL) ){ //qDebug() << " - Loop over items"; unsigned int wkspace = CurrentWorkspace(); for(unsigned int i=0; i roots){ if(DEBUG){ qDebug() << "XCB: RegisterVirtualRoots()"; } //First convert the QList into the proper format xcb_window_t *list = new xcb_window_t[ roots.length() ]; for(int i=0; iwidth, reply->height); //make sure to use the origin point for the window //qDebug() << " - "<x << reply->y << reply->width << reply->height; free(reply); if(includeFrame){ //Need to add/include the frame extents as well (assuming the frame info is available) xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win); if(cookie.sequence != 0){ xcb_ewmh_get_extents_reply_t frame; if(1== xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &frame, NULL) ){ //adjust the origin point to account for the frame geom.translate(-frame.left, -frame.top); //move to the orign point for the frame //adjust the size (include the frame sizes) geom.setWidth( geom.width() + frame.left + frame.right ); geom.setHeight( geom.height() + frame.top + frame.bottom ); } //qDebug() << " - Frame:" << frame.left << frame.right << frame.top << frame.bottom; //qDebug() << " - Modified with Frame:" << geom.x() << geom.y() << geom.width() << geom.height(); } } //Now need to convert this to absolute coordinates (not parent-relavitve) xcb_translate_coordinates_cookie_t tcookie = xcb_translate_coordinates(QX11Info::connection(), win, QX11Info::appRootWindow(), geom.x(), geom.y()); xcb_translate_coordinates_reply_t *trans = xcb_translate_coordinates_reply(QX11Info::connection(), tcookie, NULL); if(trans!=0){ //qDebug() << " - Got Translation:" << trans->dst_x << trans->dst_y; //Replace the origin point with the global position (sizing remains the same) geom.moveLeft(trans->dst_x); //adjust X coordinate (no size change) geom.moveTop(trans->dst_y); //adjust Y coordinate (no size change) free(trans); } }else{ //Need to do another catch for this situation (probably not mapped yet) } return geom; } // === WindowFrameGeometry() === QList LXCB::WindowFrameGeometry(WId win){ if(DEBUG){ qDebug() << "XCB: WindowFrameGeometry()"; } //Returns: [top, bottom, left, right] sizes for the frame QList geom; if(win!=0){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win); if(cookie.sequence != 0){ xcb_ewmh_get_extents_reply_t frame; if(1== xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &frame, NULL) ){ //adjust the origin point to account for the frame geom << frame.top << frame.bottom << frame.left << frame.right; } } } if(geom.isEmpty()){ geom << 0 << 0 << 0 << 0; } return geom; } // === WindowState() === LXCB::WINDOWVISIBILITY 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; WINDOWVISIBILITY 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; imap_state==XCB_MAP_STATE_VIEWABLE){ cstate = VISIBLE; } else{ cstate = INVISIBLE; } free(attr); } } return cstate; } // === WindowVisibleIconName() === QString LXCB::WindowVisibleIconName(WId win){ //_NET_WM_VISIBLE_ICON_NAME if(DEBUG){ qDebug() << "XCB: WindowVisibleIconName()"; } if(win==0){ return ""; } QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_visible_icon_name_reply(&EWMH, cookie, &data, NULL) ){ out = QString::fromUtf8(data.strings, data.strings_len); } return out; } // === WindowIconName() === QString LXCB::WindowIconName(WId win){ //_NET_WM_ICON_NAME if(DEBUG){ qDebug() << "XCB: WindowIconName()"; } if(win==0){ return ""; } QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_icon_name_reply(&EWMH, cookie, &data, NULL) ){ out = QString::fromUtf8(data.strings, data.strings_len); } return out; } // === WindowVisibleName() === QString LXCB::WindowVisibleName(WId win){ //_NET_WM_VISIBLE_NAME if(DEBUG){ qDebug() << "XCB: WindowVisibleName()"; } if(win==0){ return ""; } QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_visible_name_reply(&EWMH, cookie, &data, NULL) ){ out = QString::fromUtf8(data.strings, data.strings_len); } return out; } // === WindowName() === QString LXCB::WindowName(WId win){ //_NET_WM_NAME if(DEBUG){ qDebug() << "XCB: WindowName()"; } if(win==0){ return ""; } QString out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name_unchecked(&EWMH, win); if(cookie.sequence == 0){ return out; } xcb_ewmh_get_utf8_strings_reply_t data; if( 1 == xcb_ewmh_get_wm_name_reply(&EWMH, cookie, &data, NULL) ){ out = QString::fromUtf8(data.strings, data.strings_len); } return out; } // === OldWindowName() === QString LXCB::OldWindowName(WId win){ //WM_NAME (old standard) if(DEBUG){ qDebug() << "XCB: OldWindowName()"; } if(win==0){ return ""; } xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win); xcb_icccm_get_text_property_reply_t reply; if(1 == xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ QString name = QString::fromLocal8Bit(reply.name, reply.name_len); xcb_icccm_get_text_property_reply_wipe(&reply); return name; }else{ return ""; } } // === OldWindowIconName() === QString LXCB::OldWindowIconName(WId win){ //WM_ICON_NAME (old standard) if(DEBUG){ qDebug() << "XCB: OldWindowIconName()"; } if(win==0){ return ""; } xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win); xcb_icccm_get_text_property_reply_t reply; if(1 == xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ QString name = QString::fromLocal8Bit(reply.name, reply.name_len); xcb_icccm_get_text_property_reply_wipe(&reply); return name; }else{ return ""; } } // === WindowIsMaximized() === bool LXCB::WindowIsMaximized(WId win){ if(DEBUG){ qDebug() << "XCB: WindowIsMaximized()"; } if(win==0){ return ""; } //See if the _NET_WM_STATE_MAXIMIZED_[VERT/HORZ] flags are set on the window xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win); if(cookie.sequence == 0){ return false; } xcb_ewmh_get_atoms_reply_t states; if( 1 == xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &states, NULL) ){ //Loop over the states for(unsigned int i=0; i screens = QGuiApplication::screens(); QList::const_iterator it; int i = 0; for(it = screens.constBegin(); it != screens.constEnd(); ++it, ++i) { QRect sgeom = (*it)->availableGeometry(); qDebug() << " -- Check Window Geom:" << sgeom << geom << this->WindowClass(win); if( sgeom.contains(geom.center()) ){ //Allow a 1 pixel variation in "full-screen" detection qDebug() << " -- Found Screen:" << i; if( geom.width() >= (sgeom.width()-1) && geom.height()>=(sgeom.height()-1) ){ qDebug() << " -- Is Fullscreen!"; //fullS = true; fscreen = i; } break; //found the screen which contains this window } } //} //return fullS; return fscreen; } // === WindowIcon() === QIcon LXCB::WindowIcon(WId win){ //Fetch the _NET_WM_ICON for the window and return it as a QIcon if(DEBUG){ qDebug() << "XCB: WindowIcon()"; } QIcon icon; if(win==0){ return icon; } xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&EWMH, win); xcb_ewmh_get_wm_icon_reply_t reply; if(1 == xcb_ewmh_get_wm_icon_reply(&EWMH, cookie, &reply, NULL)){ xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply); //Just use the first bool done =false; while(!done){ //Now convert the current data into a Qt image // - first 2 elements are width and height (removed via XCB functions) // - data in rows from left to right and top to bottom QImage image(iter.width, iter.height, QImage::Format_ARGB32); //initial setup uint* dat = iter.data; //dat+=2; //remember the first 2 element offset for(qsizetype i=0; iblack_pixel; xcb_create_gc(QX11Info::connection(), gc, screen->root, XCB_GC_BACKGROUND, values ); //Convert the QPixmap to a xcb_drawable_t QImage qimage = pix->toImage().convertToFormat(QImage::Format_ARGB32); xcb_image_t *image = xcb_image_create(pix->width(), pix->height(), XCB_IMAGE_FORMAT_XY_PIXMAP, 32, qimage.depth(), 32, 32, XCB_IMAGE_ORDER_LSB_FIRST, XCB_IMAGE_ORDER_LSB_FIRST, 0, qimage.sizeInBytes(), qimage.bits()); //Now paint on the root window xcb_image_put(QX11Info::connection(), screen->root, //where to put it gc, image, area.x(), area.y(), 0); /*xcb_copy_area(QX11Info::connection(), //static_cast( pix.handle() ) , //image to copy *image, screen->root, //where to put it gc, //graphics context for destination 0, //x origin of picture 0, // y origin of picture area.x(), //x origin of where to place it area.y(), //y origin of where to place it area.width(), area.height() );*/ //Apply the change right now xcb_flush(QX11Info::connection()); xcb_image_destroy(image); } // === SetAsSticky() === void LXCB::SetAsSticky(WId win){ if(DEBUG){ qDebug() << "XCB: SetAsSticky()"; } if(win==0){ return; } //Need to send a client message event for the window so the WM picks it up xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.window = win; event.type = EWMH._NET_WM_STATE; event.data.data32[0] = 1; //set to enabled event.data.data32[1] = EWMH._NET_WM_STATE_STICKY; event.data.data32[2] = 0; event.data.data32[3] = 0; event.data.data32[4] = 0; xcb_send_event(QX11Info::connection(), 0, QX11Info::appRootWindow(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); //This method changes the property on the window directly - the WM is not aware of it /*xcb_change_property( QX11Info::connection(), XCB_PROP_MODE_APPEND, win, EWMH._NET_WM_STATE, XCB_ATOM_ATOM, 32, 1, &(EWMH._NET_WM_STATE_STICKY) ); xcb_flush(QX11Info::connection()); //apply it right away*/ } // === SetDisableWMActions() === void LXCB::SetDisableWMActions(WId win){ if(DEBUG){ qDebug() << "XCB: SetDisableWMActions()"; } //This disables all the various control that a WM allows for the window (except for allowing the "Sticky" state) xcb_atom_t list[1]; list[0] = EWMH._NET_WM_ACTION_STICK; xcb_ewmh_set_wm_allowed_actions(&EWMH, win, 1, list); } // === SetAsPanel() === void LXCB::SetAsPanel(WId win){ if(DEBUG){ qDebug() << "XCB: SetAsPanel()"; } if(win==0){ return; } SetDisableWMActions(win); //also need to disable WM actions for this window //Disable Input focus (panel activation ruins task manager window detection routines) // - Disable Input flag in WM_HINTS xcb_icccm_wm_hints_t hints; //qDebug() << " - Disable WM_HINTS input flag"; xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(QX11Info::connection(), win); //qDebug() << " -- got cookie"; if(1 == xcb_icccm_get_wm_hints_reply(QX11Info::connection(), cookie, &hints, NULL) ){ //qDebug() << " -- Set no inputs flag"; xcb_icccm_wm_hints_set_input(&hints, false); //set no input focus xcb_icccm_set_wm_hints(QX11Info::connection(), win, &hints); //save hints back to window } // - Remove WM_TAKE_FOCUS from the WM_PROTOCOLS for the window // - - Generate the necessary atoms //qDebug() << " - Generate WM_PROTOCOLS and WM_TAKE_FOCUS atoms"; xcb_atom_t WM_PROTOCOLS, WM_TAKE_FOCUS; //the two atoms needed xcb_intern_atom_reply_t *preply = xcb_intern_atom_reply(QX11Info::connection(), \ xcb_intern_atom(QX11Info::connection(), 0, 12, "WM_PROTOCOLS"), NULL); xcb_intern_atom_reply_t *freply = xcb_intern_atom_reply(QX11Info::connection(), \ xcb_intern_atom(QX11Info::connection(), 0, 13, "WM_TAKE_FOCUS"), NULL); bool gotatoms = false; if(preply && freply){ WM_PROTOCOLS = preply->atom; WM_TAKE_FOCUS = freply->atom; free(preply); free(freply); gotatoms = true; //qDebug() << " -- success"; } // - - Now update the protocols for the window if(gotatoms){ //requires the atoms //qDebug() << " - Get WM_PROTOCOLS"; xcb_icccm_get_wm_protocols_reply_t proto; if( 1 == xcb_icccm_get_wm_protocols_reply(QX11Info::connection(), \ xcb_icccm_get_wm_protocols_unchecked(QX11Info::connection(), win, WM_PROTOCOLS), \ &proto, NULL) ){ //Found the current protocols, see if it has the focus atom set //remove the take focus atom and re-save them bool needremove = false; //Note: This first loop is required so that we can initialize the modified list with a valid size //qDebug() << " -- Check current protocols"; for(unsigned int i=0; i(width), static_cast(height) }; xcb_configure_window(QX11Info::connection(), win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); } // === ReserveLocation === void LXCB::ReserveLocation(WId win, QRect geom, QString loc){ loc = loc.toLower().simplified(); //Put the values in the proper structures xcb_ewmh_wm_strut_partial_t LOC; //Initialize the structure to zeros LOC.left = LOC.right = LOC.top = LOC.bottom = 0; //initial setting LOC.left_start_y = LOC.left_end_y = LOC.right_start_y = LOC.right_end_y = 0; LOC.top_start_x = LOC.top_end_x = LOC.bottom_start_x = LOC.bottom_end_x = 0; //Now put the values into the structure based on location if(loc=="top"){ //top of screen LOC.top = geom.height(); //top width LOC.top_start_x = geom.x(); //top x start LOC.top_end_x = geom.x()+geom.width(); //top x end }else if(loc=="bottom"){ //bottom of screen LOC.bottom = geom.height(); //bottom width LOC.bottom_start_x = geom.x(); //bottom x start LOC.bottom_end_x = geom.x()+geom.width(); //bottom x end }else if(loc=="left"){ LOC.left = geom.width(); LOC.left_start_y = geom.y(); LOC.left_end_y = geom.y()+geom.height(); }else{ //right LOC.right = geom.width(); LOC.right_start_y = geom.y(); LOC.right_end_y = geom.y()+geom.height(); } //Change the property xcb_ewmh_set_wm_strut_partial(&EWMH, win, LOC); //_NET_WM_STRUT_PARTIAL (not always used) xcb_ewmh_set_wm_strut(&EWMH, win, LOC.left, LOC.right, LOC.top, LOC.bottom); //_NET_WM_STRUT } /*void LXCB::SetWindowBackground(QWidget *parent, QRect area, WId client){ //Copy the image from the parent onto the client (parent/child - for system tray apps) //First create the background graphics context //qDebug() << "Create graphics context"; //xcb_screen_t *root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen()); uint32_t val = XCB_GX_CLEAR; xcb_gcontext_t graphic_context = xcb_generate_id(QX11Info::connection()); xcb_create_gc(QX11Info::connection(), graphic_context, client, XCB_GC_BACKGROUND | XCB_GC_FOREGROUND, &val); //qDebug() << "Copy Background Area"; //Now copy the image onto the client background xcb_copy_area(QX11Info::connection(), parent->winId(), client, graphic_context, area.x(), area.y(), 0, 0, area.width(), area.height()); //Now re-map the client so it paints on top of the new background //qDebug() << "Map Window"; //xcb_map_window(QX11Info::connection(), client); //Clean up variables xcb_free_gc(QX11Info::connection(), graphic_context); }*/ // === EmbedWindow() === uint LXCB::EmbedWindow(WId win, WId container){ if(DEBUG){ qDebug() << "XCB: EmbedWindow()"; } //This returns the damage control ID number (or 0 for a failure) if(win==0 || container==0 || LXCB::WindowClass(win).isEmpty() ){ return 0; } //invalid window (destroyed before getting here?) //qDebug() << "Embed Window:" << win << container; //Initialize any atoms that will be needed xcb_intern_atom_cookie_t ecookie = xcb_intern_atom_unchecked(QX11Info::connection(), 0, 7, "_XEMBED"); xcb_intern_atom_reply_t *ereply = xcb_intern_atom_reply(QX11Info::connection(), ecookie, NULL); if(ereply==0){ return 0; } //unable to initialize the atom xcb_atom_t emb = ereply->atom; free(ereply); //done with this structure //Reparent the window into the container xcb_reparent_window(QX11Info::connection(), win, container, 0, 0); xcb_map_window(QX11Info::connection(), win); //Now send the embed event to the app //qDebug() << " - send _XEMBED event"; xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.window = win; event.type = emb; //_XEMBED event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY event.data.data32[2] = 0; event.data.data32[3] = container; //WID of the container event.data.data32[4] = 0; xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); //Now setup any redirects and return this->SelectInput(win, true); //Notify of structure changes xcb_composite_redirect_window(QX11Info::connection(), win, XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]); //Now map the window (will be a transparent child of the container) xcb_map_window(QX11Info::connection(), win); //Now create/register the damage handler // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore) //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer //xcb_damage_create(QX11Info::connection(), dmgID, win, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself). Damage dmgID = XDamageCreate(QX11Info::display(), win, XDamageReportRawRectangles); //qDebug() << " - Done"; return ( (uint) dmgID ); } // === Unembed Window() === bool LXCB::UnembedWindow(WId win){ if(DEBUG){ qDebug() << "XCB: UnembedWindow()"; } if(win==0){ return false; } //Remove redirects uint32_t val[] = {XCB_EVENT_MASK_NO_EVENT}; xcb_change_window_attributes(QX11Info::connection(), win, XCB_CW_EVENT_MASK, val); //Make sure it is invisible xcb_unmap_window(QX11Info::connection(), win); //Reparent the window back to the root window xcb_reparent_window(QX11Info::connection(), win, QX11Info::appRootWindow(), 0, 0); return true; } // === TrayImage() === QPixmap LXCB::TrayImage(WId win){ QPixmap pix; //Get the current QScreen (for XCB->Qt conversion) QList scrnlist = QApplication::screens(); if(scrnlist.isEmpty()){ return pix; } //Try to grab the given window directly with Qt if(pix.isNull()){ pix = scrnlist[0]->grabWindow(win); } return pix; //NOTE: Code below here saved for reference later (as necessary) // ------------------------------- /*//First get the pixmap from the XCB compositing layer (since the tray images are redirected there) xcb_pixmap_t pixmap = xcb_generate_id(QX11Info::connection()); xcb_composite_name_window_pixmap(QX11Info::connection(), win, pixmap); //Get the sizing information about the pixmap xcb_get_geometry_cookie_t Gcookie = xcb_get_geometry_unchecked(QX11Info::connection(), pixmap); xcb_get_geometry_reply_t *Greply = xcb_get_geometry_reply(QX11Info::connection(), Gcookie, NULL); if(Greply==0){ qDebug() << "[Tray Image] - Geom Fetch Error:"; return QPixmap(); } //Error in geometry detection //Now convert the XCB pixmap into an XCB image xcb_get_image_cookie_t GIcookie = xcb_get_image_unchecked(QX11Info::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, 0, 0, Greply->width, Greply->height, 0xffffffff); xcb_get_image_reply_t *GIreply = xcb_get_image_reply(QX11Info::connection(), GIcookie, NULL); if(GIreply==0){ qDebug() << "[Tray Image] - Image Convert Error:"; return QPixmap(); } //Error in conversion uint8_t *GIdata = xcb_get_image_data(GIreply); uint32_t BPL = xcb_get_image_data_length(GIreply) / Greply->height; //bytes per line //Now convert the XCB image into a Qt Image QImage image(const_cast(GIdata), Greply->width, Greply->height, BPL, QImage::Format_ARGB32_Premultiplied); //Free the various data structures free(GIreply); //done with get image reply xcb_free_pixmap(QX11Info::connection(), pixmap); //done with the raw pixmap free(Greply); //done with geom reply*/ /* NOTE: Found these little bit in the Qt sources - not sure if it is needed, but keep it here for reference // we may have to swap the byte order based on system type uint8_t image_byte_order = connection->setup()->image_byte_order; if ((QSysInfo::ByteOrder == QSysInfo::LittleEndian && image_byte_order == XCB_IMAGE_ORDER_MSB_FIRST) || (QSysInfo::ByteOrder == QSysInfo::BigEndian && image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST)) { for (int i=0; i < image.height(); i++) { uint *p = (uint*)image.scanLine(i); uint *end = p + image.width(); while (p < end) { *p = ((*p << 24) & 0xff000000) | ((*p << 8) & 0x00ff0000) | ((*p >> 8) & 0x0000ff00) | ((*p >> 24) & 0x000000ff); p++; } } }*/ // fix-up alpha channel /*if (image.format() == QImage::Format_RGB32) { QRgb *p = (QRgb *)image.bits(); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) p[x] |= 0xff000000; p += bytes_per_line / 4; }*/ //Convert the QImage into a QPixmap and return it //return QPixmap::fromImage(image.copy()); } // ===== startSystemTray() ===== WId LXCB::startSystemTray(int screen){ qDebug() << "Starting System Tray:" << screen; //Setup the freedesktop standards compliance //Get the appropriate atom for this screen QString str = QString("_NET_SYSTEM_TRAY_S%1").arg(QString::number(screen)); //qDebug() << "Default Screen Atom Name:" << str; xcb_intern_atom_reply_t *treply = xcb_intern_atom_reply(QX11Info::connection(), \ xcb_intern_atom(QX11Info::connection(), 0, str.length(), str.toLocal8Bit()), NULL); xcb_intern_atom_reply_t *oreply = xcb_intern_atom_reply(QX11Info::connection(), \ xcb_intern_atom(QX11Info::connection(), 0, 28, "_NET_SYSTEM_TRAY_ORIENTATION"), NULL); xcb_intern_atom_reply_t *vreply = xcb_intern_atom_reply(QX11Info::connection(), \ xcb_intern_atom(QX11Info::connection(), 0, 23, "_NET_SYSTEM_TRAY_VISUAL"), NULL); if(treply==0){ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_S atom"; return 0; } if(oreply==0){ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_ORIENTATION atom"; return 0; } if(vreply==0){ qDebug() << " - ERROR: Could not initialize _NET_SYSTEM_TRAY_VISUAL atom"; return 0; } xcb_atom_t _NET_SYSTEM_TRAY_S = treply->atom; xcb_atom_t _NET_SYSTEM_TRAY_ORIENTATION = oreply->atom; xcb_atom_t _NET_SYSTEM_TRAY_VISUAL = vreply->atom; free(treply); //done with atom generation free(oreply); free(vreply); //Make sure that there is no other system tray running xcb_get_selection_owner_reply_t *ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL); if(ownreply==0){ qWarning() << " - Could not get owner selection reply"; return 0; } if(ownreply->owner != 0){ free(ownreply); qWarning() << " - An alternate system tray is currently in use"; return 0; } free(ownreply); //Create a simple window to register as the tray (not visible - just off the screen) xcb_screen_t *root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen()); uint32_t params[] = {1}; WId LuminaSessionTrayID = xcb_generate_id(QX11Info::connection()); //need a new ID xcb_create_window(QX11Info::connection(), root_screen->root_depth, \ LuminaSessionTrayID, root_screen->root, -1, -1, 1, 1, 0, \ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \ XCB_CW_OVERRIDE_REDIRECT, params); //Now register this widget as the system tray xcb_set_selection_owner(QX11Info::connection(), LuminaSessionTrayID, _NET_SYSTEM_TRAY_S, XCB_CURRENT_TIME); //Make sure that it was registered properly ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL); if(ownreply==0 || ownreply->owner != LuminaSessionTrayID){ if(ownreply!=0){ free(ownreply); } qWarning() << " - Could not register the system tray"; xcb_destroy_window(QX11Info::connection(), LuminaSessionTrayID); return 0; } free(ownreply); //done with structure //Now register the orientation of the system tray uint32_t orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ; xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, LuminaSessionTrayID, \ _NET_SYSTEM_TRAY_ORIENTATION, XCB_ATOM_CARDINAL, 32, 1, &orient); //Now set the visual ID for the system tray (same as the root window, but TrueColor) xcb_visualtype_t *type = xcb_aux_find_visual_by_attrs(root_screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32); if(type!=0){ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, LuminaSessionTrayID, \ _NET_SYSTEM_TRAY_VISUAL, XCB_ATOM_VISUALID, 32, 1, &type->visual_id); }else{ qWarning() << " - Could not set TrueColor visual for system tray"; } //Finally, send out an X event letting others know that the system tray is up and running xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.window = root_screen->root; event.type = EWMH.MANAGER; //MANAGER atom event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime; event.data.data32[1] = _NET_SYSTEM_TRAY_S; //_NET_SYSTEM_TRAY_S atom event.data.data32[2] = LuminaSessionTrayID; event.data.data32[3] = 0; event.data.data32[4] = 0; xcb_send_event(QX11Info::connection(), 0, root_screen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); //Success return LuminaSessionTrayID; } // ===== closeSystemTray() ===== void LXCB::closeSystemTray(WId trayID){ xcb_destroy_window(QX11Info::connection(), trayID); } // === SetScreenWorkArea() === /*void LXCB::SetScreenWorkArea(unsigned int screen, QRect rect){ //This is only useful because Fluxbox does not set the _NET_WORKAREA root atom // This needs to be better incorporated into the new window manager later //First get the current workarea array (for all monitors/screens) xcb_get_property_cookie_t cookie = xcb_ewmh_get_workarea_unchecked(&EWMH, 0); xcb_ewmh_get_workarea_reply_t work; if(0==xcb_ewmh_get_workarea_reply(&EWMH, cookie, &work, NULL)){ return; } //Error: Could not retrieve current work areas //Save what we need only from the reply unsigned int desks = work.workarea_len; if(desks <= screen){ return; } //invalid screen to modify qDebug() << "Number of desktops/screens:" << desks; xcb_ewmh_geometry_t *dareas = work.workarea; //Adjust the work area for the input monitor/screen dareas[screen].x = rect.x(); dareas[screen].y = rect.y(); dareas[screen].width = rect.width(); dareas[screen].height = rect.height(); //Now save the array again xcb_ewmh_set_workarea(&EWMH, 0, desks, dareas); //_NET_WORKAREA //Make sure to clear that reply xcb_ewmh_get_workarea_reply_wipe(&work); }*/ //============ // WM Functions (directly changing properties/settings) // - Using these directly may prevent the WM from seeing the change //============ void LXCB::WM_CloseWindow(WId win, bool force){ if(!force){ // && WM_ICCCM_GetProtocols(win).testFlag(LXCB::DELETE_WINDOW)){ //Send the window a WM_DELETE_WINDOW message if(atoms.isEmpty()){ createWMAtoms(); } //need these atoms xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.window = win; event.type = ATOMS[atoms.indexOf("WM_PROTOCOLS")]; event.data.data32[0] = ATOMS[atoms.indexOf("WM_DELETE_WINDOW")]; event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime; event.data.data32[2] = 0; event.data.data32[3] = 0; event.data.data32[4] = 0; xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event); xcb_flush(QX11Info::connection()); }else{ xcb_destroy_window(QX11Info::connection(), win); } } void LXCB::WM_ShowWindow(WId win){ xcb_map_window(QX11Info::connection(), win); } void LXCB::WM_HideWindow(WId win){ xcb_unmap_window(QX11Info::connection(), win); } QList LXCB::WM_RootWindows(){ xcb_query_tree_cookie_t cookie = xcb_query_tree(QX11Info::connection(), QX11Info::appRootWindow()); xcb_query_tree_reply_t *reply = 0; QList out; reply=xcb_query_tree_reply(QX11Info::connection(), cookie, NULL); if(reply!=0){ int num = xcb_query_tree_children_length(reply); xcb_window_t *children = xcb_query_tree_children(reply); for(int i=0; iroot_depth, \ win, parent, -1, -1, 1, 1, 0, \ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \ XCB_CW_OVERRIDE_REDIRECT, params); return win; } bool LXCB::WM_ManageWindow(WId win, bool needsmap){ #define CLIENT_WIN_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ XCB_EVENT_MASK_FOCUS_CHANGE) //return whether the window is/should be managed if(WM_ICCCM_GetClass(win).isEmpty() ){ return false; } 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){ return false; } //could not get attributes of window if(attr->override_redirect){ free(attr); return false; } //window has override redirect set (do not manage) if(!needsmap && attr->map_state != XCB_MAP_STATE_VIEWABLE){ //window is never supposed to be visible (lots of these) //if( !WM_ICCCM_GetClass(win).contains("xterm") ){ //Some windows mis-set this flag qDebug() << " - Not Viewable.." << WM_ICCCM_GetClass(win); free(attr); return false; //} } //Setup event handling on the window uint32_t value_list[1] = {CLIENT_WIN_EVENT_MASK}; if( xcb_request_check(QX11Info::connection(), \ xcb_change_window_attributes_checked(QX11Info::connection(), win, XCB_CW_EVENT_MASK, value_list ) ) ){ //Could not change event mask - did the window get deleted already? free(attr); qDebug() << " - Could not change event mask"; return false; } return true; } QRect LXCB::WM_Window_Geom(WId win){ xcb_get_geometry_cookie_t cookie = xcb_get_geometry_unchecked(QX11Info::connection(), win); xcb_get_geometry_reply_t *reply = 0; QRect geom; reply = xcb_get_geometry_reply(QX11Info::connection(), cookie, NULL); if(reply!=0){ geom = QRect(reply->x, reply->y, reply->width, reply->height); free(reply); } return geom; } void LXCB::setupEventsForFrame(WId frame){ #define FRAME_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_BUTTON_RELEASE | \ XCB_EVENT_MASK_POINTER_MOTION | \ XCB_EVENT_MASK_BUTTON_MOTION | \ XCB_EVENT_MASK_EXPOSURE | \ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ XCB_EVENT_MASK_ENTER_WINDOW) uint32_t value_list[1] = {FRAME_WIN_EVENT_MASK}; xcb_change_window_attributes(QX11Info::connection(), frame, XCB_CW_EVENT_MASK, value_list); } bool LXCB::setupEventsForRoot(WId root){ #define ROOT_WIN_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ XCB_EVENT_MASK_BUTTON_PRESS | \ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \ XCB_EVENT_MASK_POINTER_MOTION | \ XCB_EVENT_MASK_PROPERTY_CHANGE | \ XCB_EVENT_MASK_FOCUS_CHANGE | \ XCB_EVENT_MASK_ENTER_WINDOW) if(root==0){ root = QX11Info::appRootWindow(); } uint32_t value_list[1] = {ROOT_WIN_EVENT_MASK}; xcb_generic_error_t *status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), root, XCB_CW_EVENT_MASK, value_list)); return (status==0); } // -------------------------------------------------- // ICCCM Standards (older standards) // -------------------------------------------------- // -- WM_NAME QString LXCB::WM_ICCCM_GetName(WId win){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win); xcb_icccm_get_text_property_reply_t reply; if(1 != xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ return ""; //error in fetching name }else{ return QString::fromLocal8Bit(reply.name); } } void LXCB::WM_ICCCM_SetName(WId win, QString name){ xcb_icccm_set_wm_name(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit()); } // -- WM_ICON_NAME QString LXCB::WM_ICCCM_GetIconName(WId win){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win); xcb_icccm_get_text_property_reply_t reply; if(1 != xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){ return ""; //error in fetching name }else{ return QString::fromLocal8Bit(reply.name); } } void LXCB::WM_ICCCM_SetIconName(WId win, QString name){ xcb_icccm_set_wm_icon_name(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit()); } // -- WM_CLIENT_MACHINE QString LXCB::WM_ICCCM_GetClientMachine(WId win){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_client_machine_unchecked(QX11Info::connection(), win); xcb_icccm_get_text_property_reply_t reply; if(1 != xcb_icccm_get_wm_client_machine_reply(QX11Info::connection(), cookie, &reply, NULL) ){ return ""; //error in fetching name }else{ return QString::fromLocal8Bit(reply.name); } } void LXCB::WM_ICCCM_SetClientMachine(WId win, QString name){ xcb_icccm_set_wm_client_machine(QX11Info::connection(), win, XCB_ATOM_STRING, 8, name.length(), name.toLocal8Bit()); } // -- WM_CLASS QString LXCB::WM_ICCCM_GetClass(WId win){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class_unchecked(QX11Info::connection(), win); xcb_icccm_get_wm_class_reply_t reply; if(1 != xcb_icccm_get_wm_class_reply(QX11Info::connection(), cookie, &reply, NULL) ){ return ""; //error in fetching name }else{ //Returns: "::::" return ( QString::fromLocal8Bit(reply.instance_name)+"::::"+QString::fromLocal8Bit(reply.class_name) ); } } void LXCB::WM_ICCCM_SetClass(WId win, QString name){ xcb_icccm_set_wm_class(QX11Info::connection(), win, name.length(), name.toLocal8Bit()); } // -- WM_TRANSIENT_FOR WId LXCB::WM_ICCCM_GetTransientFor(WId win){ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for_unchecked(QX11Info::connection(), win); xcb_window_t trans; if(1!= xcb_icccm_get_wm_transient_for_reply(QX11Info::connection(), cookie, &trans, NULL) ){ return win; //error in fetching transient window ID (or none found) }else{ return trans; } } void LXCB::WM_ICCCM_SetTransientFor(WId win, WId transient){ xcb_icccm_set_wm_transient_for(QX11Info::connection(), win, transient); } // -- WM_SIZE_HINTS (older property?) icccm_size_hints LXCB::WM_ICCCM_GetSizeHints(WId win){ //most values in structure are -1 if not set icccm_size_hints hints; xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_size_hints_unchecked(QX11Info::connection(), win, XCB_ATOM_WM_SIZE_HINTS); xcb_size_hints_t reply; if(1==xcb_icccm_get_wm_size_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){ //Now go though and move any data into the output struct if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_POSITION)==XCB_ICCCM_SIZE_HINT_US_POSITION ){ hints.x=reply.x; hints.y=reply.y; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_SIZE)==XCB_ICCCM_SIZE_HINT_US_SIZE ){ hints.width=reply.width; hints.height=reply.height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_POSITION)==XCB_ICCCM_SIZE_HINT_P_POSITION ){ hints.x=reply.x; hints.y=reply.y; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_SIZE)==XCB_ICCCM_SIZE_HINT_P_SIZE ){ hints.width=reply.width; hints.height=reply.height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)==XCB_ICCCM_SIZE_HINT_P_MIN_SIZE ){ hints.min_width=reply.min_width; hints.min_height=reply.min_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)==XCB_ICCCM_SIZE_HINT_P_MAX_SIZE ){ hints.max_width=reply.max_width; hints.max_height=reply.max_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)==XCB_ICCCM_SIZE_HINT_P_RESIZE_INC ){ hints.width_inc=reply.width_inc; hints.height_inc=reply.height_inc; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_ASPECT)==XCB_ICCCM_SIZE_HINT_P_ASPECT ){ hints.min_aspect_num=reply.min_aspect_num; hints.min_aspect_den=reply.min_aspect_den; hints.max_aspect_num=reply.max_aspect_num; hints.max_aspect_den=reply.max_aspect_den;} if( (reply.flags&XCB_ICCCM_SIZE_HINT_BASE_SIZE)==XCB_ICCCM_SIZE_HINT_BASE_SIZE ){ hints.base_width=reply.base_width; hints.base_height=reply.base_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)==XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY ){ hints.win_gravity=reply.win_gravity; } //free(reply); } return hints; } //void WM_ICCCM_SetSizeHints(WId win, icccm_size_hints hints); // -- WM_NORMAL_HINTS (newer property? - check for this before falling back on WM_SIZE_HINTS) icccm_size_hints LXCB::WM_ICCCM_GetNormalHints(WId win){ //most values in structure are -1 if not set icccm_size_hints hints; xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_normal_hints_unchecked(QX11Info::connection(), win); xcb_size_hints_t reply; if(1==xcb_icccm_get_wm_normal_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){ //Now go though and move any data into the output struct if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_POSITION)==XCB_ICCCM_SIZE_HINT_US_POSITION ){ hints.x=reply.x; hints.y=reply.y; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_SIZE)==XCB_ICCCM_SIZE_HINT_US_SIZE ){ hints.width=reply.width; hints.height=reply.height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_POSITION)==XCB_ICCCM_SIZE_HINT_P_POSITION ){ hints.x=reply.x; hints.y=reply.y; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_SIZE)==XCB_ICCCM_SIZE_HINT_P_SIZE ){ hints.width=reply.width; hints.height=reply.height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)==XCB_ICCCM_SIZE_HINT_P_MIN_SIZE ){ hints.min_width=reply.min_width; hints.min_height=reply.min_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)==XCB_ICCCM_SIZE_HINT_P_MAX_SIZE ){ hints.max_width=reply.max_width; hints.max_height=reply.max_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)==XCB_ICCCM_SIZE_HINT_P_RESIZE_INC ){ hints.width_inc=reply.width_inc; hints.height_inc=reply.height_inc; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_ASPECT)==XCB_ICCCM_SIZE_HINT_P_ASPECT ){ hints.min_aspect_num=reply.min_aspect_num; hints.min_aspect_den=reply.min_aspect_den; hints.max_aspect_num=reply.max_aspect_num; hints.max_aspect_den=reply.max_aspect_den;} if( (reply.flags&XCB_ICCCM_SIZE_HINT_BASE_SIZE)==XCB_ICCCM_SIZE_HINT_BASE_SIZE ){ hints.base_width=reply.base_width; hints.base_height=reply.base_height; } if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)==XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY ){ hints.win_gravity=reply.win_gravity; } //free(reply); } return hints; } /*void LXCB::WM_ICCCM_SetNormalHints(WId win, icccm_size_hints hints){ //Convert the data structure into the proper format xcb_size_hints_t xhints; if(hints.x>=0 || hints.y>=0){ xcb_icccm_size_hints_set_position(&xhints, 1, hints.x, hints.y); } //if(hints.width>=0 xcb_icccm_set_wm_normal_hints(QX11Info::connection(), win, &xhints); }*/ // -- WM_HINTS // -- WM_PROTOCOLS LXCB::ICCCM_PROTOCOLS LXCB::WM_ICCCM_GetProtocols(WId win){ if(atoms.isEmpty()){ createWMAtoms(); } xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_protocols(QX11Info::connection(), win, EWMH.WM_PROTOCOLS); xcb_icccm_get_wm_protocols_reply_t reply; LXCB::ICCCM_PROTOCOLS flags; if(1==xcb_icccm_get_wm_protocols_reply(QX11Info::connection(), cookie, &reply, NULL) ){ for(unsigned int i=0; i LXCB::WM_Get_Client_List(bool stacking){ QList out; if(stacking){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_client_list_stacking(&EWMH, QX11Info::appScreen()); xcb_ewmh_get_windows_reply_t reply; if(1==xcb_ewmh_get_client_list_stacking_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list, bool stacking){ //convert the QList into a generic array xcb_window_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Desktop_Viewport(){ QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_desktop_viewport_unchecked(&EWMH, QX11Info::appScreen()); xcb_ewmh_get_desktop_viewport_reply_t reply; if(1==xcb_ewmh_get_desktop_viewport_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Turn the QList into xcb_ewmh_coordinates_t* xcb_ewmh_coordinates_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Workarea(){ QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_workarea_unchecked(&EWMH, QX11Info::appScreen()); xcb_ewmh_get_workarea_reply_t reply; if(1==xcb_ewmh_get_workarea_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Convert to the XCB/EWMH data structures xcb_ewmh_geometry_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Virtual_Roots(){ QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_virtual_roots_unchecked(&EWMH, QX11Info::appScreen()); xcb_ewmh_get_windows_reply_t reply; if(1==xcb_ewmh_get_virtual_roots_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Convert to XCB array xcb_window_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Window_Type(WId win){ // Note: This will silently discard any unknown/non-standard window type flags // The client should ensure to set at least one standardized type flag per the specifications. QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_window_type_unchecked(&EWMH, win); xcb_ewmh_get_atoms_reply_t reply; if(1==xcb_ewmh_get_wm_window_type_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Convert to the XCB format xcb_atom_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Window_States(WId win){ QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_state_unchecked(&EWMH, win); xcb_ewmh_get_atoms_reply_t reply; if(1==xcb_ewmh_get_wm_state_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Convert to the XCB format xcb_atom_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Window_Actions(WId win){ QList out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_allowed_actions_unchecked(&EWMH, win); xcb_ewmh_get_atoms_reply_t reply; if(1==xcb_ewmh_get_wm_allowed_actions_reply(&EWMH, cookie, &reply, NULL) ){ for(unsigned int i=0; i list){ //Convert to the XCB format xcb_atom_t array[list.length()]; for(int i=0; i LXCB::WM_Get_Window_Strut(WId win){ //Returns: [left,right,top,bottom] margins in pixels (always length 4) QList out; out << 0 << 0 << 0 << 0; //init the output list xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_strut_unchecked(&EWMH, win); xcb_ewmh_get_extents_reply_t reply; if(1==xcb_ewmh_get_wm_strut_reply(&EWMH, cookie, &reply, NULL) ){ out[0] = reply.left; out[1] = reply.right; out[2] = reply.top; out[3] = reply.bottom; } return out; } void LXCB::WM_Set_Window_Strut(WId win, QList margins){ //Input: [left, right, top, bottom] - must be length 4 while(margins.length()<4){ margins << 0; } xcb_ewmh_set_wm_strut(&EWMH, win, margins[0], margins[1], margins[2], margins[3]); } // _NET_WM_STRUT_PARTIAL QList LXCB::WM_Get_Window_Strut_Partial(WId win){ //Returns: [left,right,top,bottom] struts QList out; out << strut_geom() << strut_geom() << strut_geom() << strut_geom(); xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_strut_partial_unchecked(&EWMH, win); xcb_ewmh_wm_strut_partial_t reply; if(1==xcb_ewmh_get_wm_strut_partial_reply(&EWMH, cookie, &reply, NULL) ){ if(reply.left>0){ out[0].start = reply.left_start_y; out[0].end = reply.left_end_y; out[0].thick = reply.left; } if(reply.right>0){ out[1].start = reply.right_start_y; out[1].end = reply.right_end_y; out[1].thick = reply.right; } if(reply.top>0){ out[2].start = reply.top_start_x; out[2].end = reply.top_end_x; out[2].thick = reply.top; } if(reply.bottom>0){ out[3].start = reply.bottom_start_x; out[3].end = reply.bottom_end_x; out[3].thick = reply.bottom; } } return out; } void LXCB::WM_Set_Window_Strut_Partial(WId win, QList struts){ //Input: [left,right,top,bottom] - must be length 4 while(struts.length() < 4){ struts << strut_geom(); } //Convert to the XCB input format xcb_ewmh_wm_strut_partial_t input; input.left=struts[0].thick; input.left_start_y=struts[0].start; input.left_end_y=struts[0].end; input.right=struts[1].thick; input.right_start_y=struts[1].start; input.right_end_y=struts[1].end; input.top=struts[2].thick; input.top_start_x=struts[2].start; input.top_end_x=struts[2].end; input.bottom=struts[3].thick; input.bottom_start_x=struts[3].start; input.bottom_end_x=struts[3].end; //Now set the property xcb_ewmh_set_wm_strut_partial(&EWMH, win, input); } // _NET_WM_ICON_GEOMETRY QRect LXCB::WM_Get_Icon_Geometry(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_geometry_unchecked(&EWMH, win); xcb_ewmh_geometry_t reply; QRect out; if(1==xcb_ewmh_get_wm_icon_geometry_reply(&EWMH, cookie, &reply, NULL) ){ out = QRect(reply.x, reply.y, reply.width, reply.height); } return out; } void LXCB::WM_Set_Icon_Geometry(WId win, QRect geom){ //Note - 11/12/15: xcb_ewmh.h lists the inputs as "left/right/top/bottom" // but this might be an error and the real inputs are "x/y/width/height" // as in the other geometry get/set routines (and as returned by the xcb_ewmh_get_wm_icon_geometry() routine) xcb_ewmh_set_wm_icon_geometry(&EWMH, win, geom.x(), geom.x()+geom.width(), geom.y(), geom.y()+geom.height()); //xcb_ewmh_set_wm_icon_geometry(&EWMH, win, geom.x(), geom.y(), geom.width(), geom.height()); } // _NET_WM_ICON QIcon LXCB::WM_Get_Icon(WId win){ //Note: The output is a QIcon because it allows for multiple varying-sized images to be loaded/used later as needed // For each pixmap found here, add it (in its native size) to the icon structure QIcon out; xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&EWMH, win); xcb_ewmh_get_wm_icon_reply_t reply; if(1==xcb_ewmh_get_wm_icon_reply(&EWMH, cookie, &reply, NULL) ){ //Now iterate over all the pixmaps and load them into the QIcon xcb_ewmh_wm_icon_iterator_t it = xcb_ewmh_get_wm_icon_iterator(&reply); while(it.index < reply.num_icons){ QImage img( (const unsigned char *) it.data, it.width, it.height, QImage::Format_ARGB32); out.addPixmap( QPixmap::fromImage(img) ); if(it.rem>0){ xcb_ewmh_get_wm_icon_next(&it); } //go to the next pixmap else{ break; } //just finished the last one - ensure this breaks out now (just in case) } //Clean up any background buffer for the reply xcb_ewmh_get_wm_icon_reply_wipe(&reply); } return out; } // _NET_WM_PID unsigned int LXCB::WM_Get_Pid(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_pid_unchecked(&EWMH, win); uint32_t pid = 0; xcb_ewmh_get_wm_pid_reply(&EWMH, cookie, &pid, NULL); return pid; } // _NET_WM_HANDLED_ICONS bool LXCB::WM_Get_Handled_Icons(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_handled_icons_unchecked(&EWMH, win); uint32_t num = 0; xcb_ewmh_get_wm_handled_icons_reply(&EWMH, cookie, &num, NULL); return (num!=0); //This flag is set on the window } void LXCB::WM_Set_Handled_Icons(WId win, bool set){ xcb_ewmh_set_wm_handled_icons(&EWMH, win, (set ? 1 : 0)); } // _NET_WM_USER_TIME unsigned int LXCB::WM_Get_User_Time(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_user_time_unchecked(&EWMH, win); uint32_t out = 0; xcb_ewmh_get_wm_user_time_reply(&EWMH, cookie, &out, NULL); return out; } void LXCB::WM_Set_User_Time(WId win, unsigned int xtime){ xcb_ewmh_set_wm_user_time(&EWMH, win, xtime); } // _NET_WM_USER_TIME_WINDOW /* WId LXCB::WM_Get_User_Time_WIndow(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_user_time_window_unchecked(&EWMH, win); xcb_window_t out; xcb_ewmh_get_user_time_window_reply(&EWMH, cookie, &out, NULL); return out; } void LXCB::WM_Set_User_Time_Window(WId win, WId utwin){ xcb_ewmh_set_wm_user_time_window(&EWMH, win, utwin); }*/ // _NET_FRAME_EXTENTS QList LXCB::WM_Get_Frame_Extents(WId win){ //Returns: [left,right,top,bottom] margins in pixels (always length 4) QList out; out << 0 << 0 << 0 << 0; //init the output list xcb_get_property_cookie_t cookie = xcb_ewmh_get_frame_extents_unchecked(&EWMH, win); xcb_ewmh_get_extents_reply_t reply; if(1==xcb_ewmh_get_frame_extents_reply(&EWMH, cookie, &reply, NULL) ){ out[0] = reply.left; out[1] = reply.right; out[2] = reply.top; out[3] = reply.bottom; } return out; } void LXCB::WM_Set_Frame_Extents(WId win, QList margins){ //Input: [left, right, top, bottom] - must be length 4 while(margins.length()<4){ margins << 0; } xcb_ewmh_set_frame_extents(&EWMH, win, margins[0], margins[1], margins[2], margins[3]); } // _NET_WM_OPAQUE_REGION // _NET_WM_BYPASS_COMPOSITOR // === SPECIAL WM PROTOCOLS (EWMH) === // _NET_WM_PING void LXCB::WM_Send_Ping(WId win){ xcb_ewmh_send_wm_ping(&EWMH, win, XCB_TIME_CURRENT_TIME); } // _NET_WM_SYNC_REQUEST uint64_t LXCB::WM_Get_Sync_Request_Counter(WId win){ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_sync_request_counter_unchecked(&EWMH, win); uint64_t count = 0; xcb_ewmh_get_wm_sync_request_counter_reply(&EWMH, cookie, &count, NULL); return count; } /*void LXCB::WM_Set_Sync_Request_Counter(WId win, uint64_t count){ }*/ // _NET_WM_FULLSCREEN_MONITORS QList LXCB::WM_Get_Fullscreen_Monitors(WId win){ //Returns: [top,bottom,left,right] monitor numbers for window to use when fullscreen QList out; out << 0 << 0 << 0 << 0; //init the output array xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_fullscreen_monitors_unchecked(&EWMH, win); xcb_ewmh_get_wm_fullscreen_monitors_reply_t reply; if(1==xcb_ewmh_get_wm_fullscreen_monitors_reply(&EWMH, cookie, &reply, NULL) ){ out[0] = reply.top; out[1] = reply.bottom; out[2] = reply.left; out[3] = reply.right; } return out; } void LXCB::WM_Set_Fullscreen_Montors(WId win, QList list){ //Input: [top,bottom,left,right] monitor numbers while(list.length()<4){ list << 0; } xcb_ewmh_set_wm_fullscreen_monitors(&EWMH, win, list[0], list[1], list[2], list[3]); } // _NET_WM_CM_S(n) WId LXCB::WM_Get_CM_Owner(){ xcb_get_selection_owner_cookie_t cookie = xcb_ewmh_get_wm_cm_owner_unchecked(&EWMH, QX11Info::appScreen()); xcb_window_t owner = 0; xcb_ewmh_get_wm_cm_owner_reply(&EWMH, cookie, &owner, NULL); return owner; } void LXCB::WM_Set_CM_Owner(WId win){ xcb_ewmh_set_wm_cm_owner(&EWMH, QX11Info::appScreen(), win, XCB_TIME_CURRENT_TIME,0,0); }