Set the EWMH _NET_WM_ICON window hint on X11 systems
authorparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Sat, 12 Jul 2008 18:28:56 +0000 (18:28 +0000)
committerparasti <parasti@78b8d119-cf0a-0410-b17c-f493084dd1d7>
Sat, 12 Jul 2008 18:28:56 +0000 (18:28 +0000)
This patch implements window manager icons with 8-bit alpha transparency
on X11 systems.  The window manager must support the _NET_WM_ICON hint
described in the freedesktop.org Extended Window Manager Hints[*]
specification.

SDL does provide a function for setting the window manager icon
(SDL_WM_SetIcon), however, its transparency support is limited to a
simple bitmask describing which pixels are fully opaque and which are
invisible.

Note that the functionality implemented here should be ported to
SDL_WM_SetIcon at some point.

[*] http://standards.freedesktop.org/wm-spec/latest/

git-svn-id: https://s.snth.net/svn/neverball/trunk@2384 78b8d119-cf0a-0410-b17c-f493084dd1d7

Makefile
ball/main.c
putt/main.c
share/syswm.c [new file with mode: 0644]
share/syswm.h [new file with mode: 0644]

index 218e358..934e785 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -163,6 +163,7 @@ BALL_OBJS := \
        share/tilt.o        \
        share/common.o      \
        share/keynames.o    \
+       share/syswm.o       \
        ball/hud.o          \
        ball/game.o         \
        ball/score.o        \
@@ -210,6 +211,7 @@ PUTT_OBJS := \
        share/text.o        \
        share/sync.o        \
        share/common.o      \
+       share/syswm.o       \
        putt/hud.o          \
        putt/game.o         \
        putt/hole.o         \
index e75c303..b816409 100644 (file)
@@ -30,6 +30,7 @@
 #include "set.h"
 #include "text.h"
 #include "tilt.h"
+#include "syswm.h"
 
 #include "st_conf.h"
 #include "st_title.h"
@@ -336,10 +337,6 @@ static void parse_args(int argc, char **argv)
 int main(int argc, char *argv[])
 {
     SDL_Joystick *joy = NULL;
-#ifndef __APPLE__
-    SDL_Surface *icon;
-#endif
-
     int t1, t0, uniform;
 
     lang_init("neverball", CONFIG_LOCALE);
@@ -409,14 +406,9 @@ int main(int argc, char *argv[])
     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 
-#ifndef __APPLE__
-    if ((icon = load_surface("icon/neverball.png")))
-    {
-        SDL_WM_SetIcon(icon, NULL);
-        free(icon->pixels);
-        SDL_FreeSurface(icon);
-    }
-#endif /* __APPLE__ */
+    /* This has to happen before mode setting... */
+
+    set_SDL_icon("icon/neverball.png");
 
     /* Initialize the video. */
 
@@ -427,6 +419,10 @@ int main(int argc, char *argv[])
         return 1;
     }
 
+    /* ...and this has to happen after it. */
+
+    set_EWMH_icon("icon/neverball.png");
+
     SDL_WM_SetCaption(TITLE, TITLE);
 
     init_state(&st_null);
index c5d0efa..2b08ba7 100644 (file)
@@ -31,6 +31,7 @@
 #include "game.h"
 #include "gui.h"
 #include "text.h"
+#include "syswm.h"
 
 #include "st_conf.h"
 #include "st_all.h"
@@ -190,9 +191,6 @@ static int loop(void)
 int main(int argc, char *argv[])
 {
     int camera = 0;
-#ifndef __APPLE__
-    SDL_Surface *icon;
-#endif
     SDL_Joystick *joy = NULL;
 
     srand((int) time(NULL));
@@ -236,14 +234,9 @@ int main(int argc, char *argv[])
                 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
                 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 
-#ifndef __APPLE__
-                if ((icon = load_surface("icon/neverputt.png")))
-                {
-                    SDL_WM_SetIcon(icon, NULL);
-                    free(icon->pixels);
-                    SDL_FreeSurface(icon);
-                }
-#endif /* __APPLE__ */
+                /* This has to happen before mode setting... */
+
+                set_SDL_icon("icon/neverputt.png");
 
                 /* Initialize the video. */
 
@@ -253,6 +246,10 @@ int main(int argc, char *argv[])
                 {
                     int t1, t0 = SDL_GetTicks();
 
+                    /* ... and this has to happen after it. */
+
+                    set_EWMH_icon("icon/neverputt.png");
+
                     SDL_WM_SetCaption(TITLE, TITLE);
 
                     /* Run the main game loop. */
diff --git a/share/syswm.c b/share/syswm.c
new file mode 100644 (file)
index 0000000..7592380
--- /dev/null
@@ -0,0 +1,125 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <SDL_video.h>
+#include <SDL_syswm.h>
+
+#include "syswm.h"
+#include "base_config.h"
+#include "image.h"
+#include "lang.h"
+
+/*---------------------------------------------------------------------------*/
+
+void set_SDL_icon(const char *filename)
+{
+#ifndef __APPLE__
+    SDL_Surface *icon;
+
+    if ((icon = load_surface(filename)))
+    {
+        SDL_WM_SetIcon(icon, NULL);
+        free(icon->pixels);
+        SDL_FreeSurface(icon);
+    }
+#endif /* __APPLE__ */
+    return;
+}
+
+void set_EWMH_icon(const char *filename)
+{
+#if SDL_VIDEO_DRIVER_X11
+    SDL_SysWMinfo info;
+
+    Display *dpy;
+    Window   window;
+
+    unsigned char *p;
+    int w, h, b;
+
+    SDL_VERSION(&info.version);
+
+    if (SDL_GetWMInfo(&info) != 1)
+    {
+        fprintf(stderr, _("Failed to get WM info: %s\n"), SDL_GetError());
+        return;
+    }
+
+    if (info.subsystem != SDL_SYSWM_X11)
+        return;
+
+    dpy    = info.info.x11.display;
+    window = info.info.x11.wmwindow;
+
+    /*
+     * This code loads an image and sets it as the _NET_WM_ICON window
+     * property, as described in the Extended Window Manager Hints
+     * specification[*].  From the spec: "This is an array of 32-bit packed
+     * CARDINAL ARGB with high byte being A, low byte being B.  The first two
+     * cardinals are width, height.  Data is in rows, left to right and top to
+     * bottom."
+     *
+     * [*] http://standards.freedesktop.org/wm-spec/latest/
+     */
+
+    if ((p = image_load(config_data(filename), &w, &h, &b)))
+    {
+        long *data = NULL;
+
+        if ((data = calloc(2 + w * h, sizeof (long))))
+        {
+            int r, c;
+
+            data[0] = w;
+            data[1] = h;
+
+            for (r = 0; r < h; r++)
+                for (c = 0; c < w; c++)
+                {
+                    long          *dp = &data[2 + r * w + c];
+                    unsigned char *pp = &p[(h - r - 1) * w * b + c * b];
+
+                    if (b < 3)
+                    {
+                        if (b == 2)
+                            *dp |= *(pp + 1) << 24;
+
+                        *dp |= *(pp + 0) << 16;
+                        *dp |= *(pp + 0) << 8;
+                        *dp |= *(pp + 0) << 0;
+                    }
+                    else
+                    {
+                        if (b == 4)
+                            *dp |= *(pp + 3) << 24;
+
+                        *dp |= *(pp + 0) << 16;
+                        *dp |= *(pp + 1) << 8;
+                        *dp |= *(pp + 2) << 0;
+                    }
+                }
+
+            info.info.x11.lock_func();
+            {
+                Atom icon = XInternAtom(dpy, "_NET_WM_ICON", False);
+
+                XChangeProperty(dpy, window, icon, XA_CARDINAL, 32,
+                                PropModeReplace, (unsigned char *) data,
+                                2 + w * h);
+            }
+            info.info.x11.unlock_func();
+
+            free(data);
+        }
+        else
+            fputs(_("Failed to allocate memory for EWMH icon data.\n"), stderr);
+
+        free(p);
+    }
+#endif
+
+    return;
+}
+
+/*---------------------------------------------------------------------------*/
+
diff --git a/share/syswm.h b/share/syswm.h
new file mode 100644 (file)
index 0000000..911be95
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef SYSWM_H
+#define SYSWM_H
+
+void set_SDL_icon (const char *);
+void set_EWMH_icon(const char *);
+
+#endif