Detect system tray\taskbar orientation(X11)

1.5k views Asked by At

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 ();
2

There are 2 answers

0
Zui On BEST ANSWER

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
}

}

2
n. m. could be an AI On

The system tray is not necessarily a window per se. In KDE, this is just an area in the taskbar (and it is unrelated to the _NET_SYSTEM_TRAY_S%i selection owner).

You may want to try embedding a tray icon and getting its geometry, then display your widget "near" said icon (for some reasonable value of "near"). You can delete the icon once you know its geometry (but then if the user moves the tray, you won't know its new coordinates).

This is not 100% reliable, as the tray is in no way obliged to show all icons you can throw at it. Also, not visually pleasing because of the icon flicker. But it's better than nothing.