Search code examples
c++qtx11taskbartray

Detect system tray\taskbar orientation(X11)


I created a TrayPopupWidget that should pops up nearby the tray. Then I realized that if the user changes the orientation, or height of the taskbar it will pops up at the wrong place. So I created the TaskbarDetector class.

I tried to get the a window geometry of the tray|taskbar, but I only get wrong window property... I tried on KDE,LXDE -> same bad behaviour...

The code: http://bzfriendsplasm.svn.sourceforge.net/viewvc/bzfriendsplasm/BZFriends/taskbardetector.cpp?revision=156&view=markup

   //Getting screen resolutoin
int num_sizes;

Rotation original_rotation;

Display *display = XOpenDisplay(NULL);

Window root = RootWindow(display, 0);

XRRScreenSize *xrrs = XRRSizes(display, 0, &num_sizes);

XRRScreenConfiguration *conf = XRRGetScreenInfo(display, root);

XRRConfigCurrentRate(conf);

SizeID original_size_id = XRRConfigCurrentConfiguration(conf, &original_rotation);

p_screenWidth = xrrs[original_size_id].width;
p_screenHeight = xrrs[original_size_id].height;


//Getting tray position

unsigned long  sysTraySelection = 0;

Screen *screen = XDefaultScreenOfDisplay(display);


//FIXME !!!
QString  *net_sys_tray = new QString("_NET_SYSTEM_TRAY_S%i");


(*net_sys_tray) = net_sys_tray->replace ("%i",QString::number (XScreenNumberOfScreen(screen)));


sysTraySelection = XInternAtom(display, net_sys_tray->toLocal8Bit (), False);

if ( sysTraySelection == None)
    return  Unknown;

trayWindow = XGetSelectionOwner(display, sysTraySelection);

XWindowAttributes w_attr;
unsigned long status = XGetWindowAttributes (display,trayWindow,&w_attr);

if ( status == 0)
    return Unknown;

p_taskBarLeft       = w_attr.y;
p_taskBarTop       = w_attr.x;
p_taskBarBottom  = w_attr.x + w_attr.height;
p_taskBarRight     = w_attr.y + w_attr.width;


qDebug () << QString("Window id:  " ) + QString::number (trayWindow);
 qDebug() << QString("SysTraySelection: ") + QString::number (sysTraySelection );
 qDebug() << QString("Top ") + QString::number (p_taskBarTop);
 qDebug() << QString("Left ") + QString::number (p_taskBarLeft);
 qDebug() << QString("Bottom ") + QString::number (p_taskBarBottom);
 qDebug() << QString("Right " ) + QString::number (p_taskBarRight);

XCloseDisplay(display);

delete net_sys_tray;

return decideOrientation ();

Solution

  • Finally, I found a way to detect it!

    1. I'm searching for a window, that has a dock property and visible, and then I call XGetWindowAttributes(...) on it.
    2. If I set a filter method to the QApplication::setEventFilter() I can get every XEvent, _NET_WORKAREA event too(this event happens when you resize or move the taskbar), then I re-call taskbar/tray detection method.

    However, this worked on KDE4 and GNOME and LXDE, I'm planning to allow the user to set the popup position by himself.

    bool TaskBarDetector::lookUpDockWindow ( unsigned long &rootWindow, bool check)
    {
    Display *display = QX11Info::display ();
    
    Window parent;
    Window *children;
    unsigned int noOfChildren;
    int status;
    
    if ( check && checkDockProperty(rootWindow)  )
    {
        trayWindow = rootWindow;
        return true;
    }
    
    status = XQueryTree (display, rootWindow, &rootWindow, &parent, &children, &noOfChildren);
    
    if (status == 0)
    {
        qDebug() << "ERROR - Could not query the window tree. Aborting.";
        trayWindow = 0;
        return false;
    }
    
    if (noOfChildren == 0)
    {
        trayWindow = 0;
        return false;
    }
    
    for (unsigned int ind = 0 ; ind < noOfChildren; ++ind )
    {
    
        if ( lookUpDockWindow ( children[ind] ,true) )
            return true;
    }
    
    XFree ((char*) children);
    
    trayWindow = 0;
    
    return false;
    }
    
    bool TaskBarDetector::checkDockProperty(unsigned long window)
    {
    Display *x11display = QX11Info::display ();
    Atom *atoms;
    int numberAtoms = 0;
    
    char *atomName;
    XTextProperty prop;
    XWindowAttributes windowattr;
    
    atoms = XListProperties (x11display, window, &numberAtoms);
    
    for (int ind = 0; ind < numberAtoms; ++ind )
    {
        atomName = XGetAtomName(x11display, atoms[ind]);
    
        if (QString(atomName).compare ("_NET_WM_WINDOW_TYPE" )  != 0 )
            continue;
    
        unsigned long status = XGetTextProperty (x11display,window,&prop,atoms[ind]);
    
        if ( status == 0 )
            continue;
    
        int value = (int) (*prop.value);
    
        if (value != 151 )
            continue;
    
        if (XGetWindowAttributes(x11display,window,&windowattr) == 0)
           continue;
    
        return windowattr.map_state == 2;
    
    }
    
    return false;
    }
    
     void TaskBarDetector::saveWindowAttr(unsigned long root)
    {
    XWindowAttributes windowattr;
    
    Display *x11display =QX11Info::display ();
    
    if (XGetWindowAttributes(x11display,trayWindow,&windowattr) == 0)
    {
        trayWindow = 0;
        return;
    }
    
    int x = 0;
    int y = 0;
    
    Window *w = &trayWindow;
    
    if(  XTranslateCoordinates(x11display,trayWindow,root,windowattr.x,windowattr.y,&x,&y,w) == True)
    {
    
        p_taskBarTop = y;
        p_taskBarLeft = x;
        p_taskBarRight = p_taskBarLeft + windowattr.width;
        p_taskBarBottom = p_taskBarTop + windowattr.height;
        p_taskBarHeight = windowattr.height;
        p_taskBarWidth = windowattr.width;
    
    } else
            {
               p_orientation           = Unknown;
               p_taskBarTop         = 0;
               p_taskBarLeft         = 0;
               p_taskBarRight       =  0;
               p_taskBarBottom    = 0;
               p_taskBarHeight     = 0;
               p_taskBarWidth      = 0;
    
            }
    
    bool TaskBarDetector::appEventFilter(void *msg, long *result)
    {
    
    
    Q_UNUSED(result);
    
    if ( !TaskBarDetector::hasInstance() )
        return false;
    
    TaskBarDetector *detector = TaskBarDetector::getInstance();
    
    #ifdef Q_WS_WIN
    MSG *seged = static_cast<MSG*>(msg);
    if ( seged->message == WM_SETTINGCHANGE && seged->wParam == SPI_SETWORKAREA )
    {
        detector->processDetectEvent();
        return false;
    }
    
    return false;
    #endif
    
    #ifdef Q_WS_X11
    XEvent *xevent = static_cast<XEvent*> (msg);
    
    
    if ( xevent->type == PropertyNotify  )
    {
        XPropertyEvent xpe = xevent->xproperty;
    
        char * ch_atom_name = XGetAtomName(QX11Info::display(),xpe.atom);
    
        QString atom_name = QString(ch_atom_name).trimmed ();
    
        if (  atom_name == "_NET_WORKAREA" )
        {
            detector->processDetectEvent ();
            return false;
        }
    
    
    }
    
    return false;
    
    #endif
    }
    

    }