Search code examples
c++wxwidgetsxlib

Get application's icon using Xlib?


I spent for about two days to reach the goal, however I didn't manage to do what I needed. My goal is to get the icon from application's window in my C++ application and save it into wxIcon (or wxImage or wxBitmap - doesn't matter) object. As I found in Internet there are two variants to do retrieve the icon: using _NET_WM_ICON window property and using XGetWMHints (by getting pixmap from XWMHints structure).

This is how I tried to implement first approach:

void ApplicationHelper::GetIcon(SRunningWindow* pDesc, void* pDisplay, 
TWindow iWindow, unsigned long uiIconAtom)
{
    unsigned long nitems, bytesafter;
    unsigned char *ret;
    int format;
    Atom type;
    XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 0, 1, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
    int width = *(int*)ret;
    XFree(ret);
    XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 1, 1, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
    int height = *(int*)ret;
    XFree(ret);
    int size = width * height;
    XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 2, size, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
    unsigned char* imgData = new unsigned char[width * height * 3]; // RGB data
    unsigned char* alphaData = new unsigned char[width * height]; // alpha chanel
    int offset = sizeof(long) == 8 ? 8 : 4; // for 64bit systems data represented in order: blue, green, red, alpha, followed by 4 zeros

    int imgIdx = 0;
    int alphaIdx = 0;
    for (int i=0; i < nitems; i += offset)
    {
        imgData[imgIdx] = ret[i + 2]; // R
        imgData[imgIdx + 1] = ret[i + 1]; // G
        imgData[imgIdx + 2] = ret[i]; // B
        alphaData[alphaIdx++] = ret[i + 3]; // A
        imgIdx += 3;
    }
    XFree(ret);
    wxImage img(width, height, imgData, alphaData);
    img.Rescale(16, 16);
    wxBitmap bmp(img);
    pDesc->icon.CopyFromBitmap(bmp);
}

But icons I get with this code are not the same as the applications have: Icons I get with this code

Regarding second variant of getting icons (getting pixmap from XGetWMHints structure) I found, that icon_pixmap field is defined as XID which is unsigned long and I didn't find how to get XPM data from this icon_pixmap.

So could you please explain what I did wrong in my code or how to get icon from 'icon_pixmap' field of XWMHints structure? Thanks.


Solution

  • Just in case if someone else will need the solution:

    void ApplicationHelper::GetIcon(SRunningWindow* pDesc, void* pDisplay, TWindow iWindow, unsigned long uiIconAtom)
    {
        unsigned long nitems, bytesafter;
        unsigned char *ret;
        int format;
        Atom type;
        XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 0, 1, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
        int width = *(int*)ret;
        XFree(ret);
        XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 1, 1, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
        int height = *(int*)ret;
        XFree(ret);
        int size = width * height;
        XGetWindowProperty((Display*) pDisplay, iWindow, uiIconAtom, 2, size, 0, AnyPropertyType, &type, &format, &nitems, &bytesafter, &ret);
        unsigned int* imgData = new unsigned int[size];
        unsigned long* ul = (unsigned long*)ret;
        for (int i=0; i < nitems; ++i)
        {
            imgData[i] = (unsigned int)ul[i];
        }
        XFree(ret);
        wxImage img(width, height);
        img.InitAlpha();
        unsigned char* argb = (unsigned char*)imgData;
        for(int y = 0; y < height; y++)
        {
            for(int x = 0; x < width; x++)
            {
                unsigned char a = argb[3];
                unsigned char r = argb[2] * a / 255;
                unsigned char g = argb[1] * a / 255;
                unsigned char b = argb[0] * a / 255;
                img.SetRGB(x, y, r, g, b);
                img.SetAlpha(x, y, a);
                argb += 4;
            }
        }
        img.Rescale(32, 32);
        wxBitmap bmp(img);
        delete[]imgData;
        pDesc->icon.CopyFromBitmap(bmp);
    }
    

    The idea is just to create wxImage from "raw" data. On x64 Ubuntu 16.04 it works