rudolfninja
rudolfninja

Reputation: 487

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.

Upvotes: 1

Views: 1003

Answers (1)

rudolfninja
rudolfninja

Reputation: 487

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

Upvotes: 1

Related Questions