Browse Source

C code: save all instead of just one client icons

Clients can provide various icons in their _NET_WM_ICON property. Up to
now we only saved a single one, now we save all of them.

Signed-off-by: Uli Schlachter <psychon@znc.in>
Uli Schlachter 2 years ago
parent
commit
f2cb8d8eb9
6 changed files with 116 additions and 72 deletions
  1. 8
    0
      draw.h
  2. 44
    45
      ewmh.c
  3. 2
    1
      ewmh.h
  4. 53
    15
      objects/client.c
  5. 3
    3
      objects/client.h
  6. 6
    8
      property.c

+ 8
- 0
draw.h View File

@@ -27,6 +27,7 @@
27 27
 #include <lua.h>
28 28
 #include <glib.h> /* for GError */
29 29
 
30
+#include "common/array.h"
30 31
 #include "common/util.h"
31 32
 
32 33
 typedef struct area_t area_t;
@@ -69,6 +70,13 @@ a_iso2utf8(const char *str, ssize_t len, char **dest, ssize_t *dlen)
69 70
     return false;
70 71
 }
71 72
 
73
+static inline void
74
+cairo_surface_array_destroy_surface(cairo_surface_t **s)
75
+{
76
+    cairo_surface_destroy(*s);
77
+}
78
+DO_ARRAY(cairo_surface_t *, cairo_surface, cairo_surface_array_destroy_surface)
79
+
72 80
 cairo_surface_t *draw_surface_from_data(int width, int height, uint32_t *data);
73 81
 cairo_surface_t *draw_dup_image_surface(cairo_surface_t *surface);
74 82
 cairo_surface_t *draw_load_image(lua_State *L, const char *path, GError **error);

+ 44
- 45
ewmh.c View File

@@ -676,63 +676,62 @@ ewmh_window_icon_get_unchecked(xcb_window_t w)
676 676
 }
677 677
 
678 678
 static cairo_surface_t *
679
-ewmh_window_icon_from_reply(xcb_get_property_reply_t *r, uint32_t preferred_size)
679
+ewmh_window_icon_from_reply_next(uint32_t **data, uint32_t *data_end)
680 680
 {
681
-    uint32_t *data, *end, *found_data = 0;
682
-    uint32_t found_size = 0;
683
-
684
-    if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || r->length < 2)
685
-        return 0;
686
-
687
-    data = (uint32_t *) xcb_get_property_value(r);
688
-    if (!data) return 0;
689
-
690
-    end = data + r->length;
691
-
692
-    /* Goes over the icon data and picks the icon that best matches the size preference.
693
-     * In case the size match is not exact, picks the closest bigger size if present,
694
-     * closest smaller size otherwise.
695
-     */
696
-    while (data + 1 < end) {
697
-        /* check whether the data size specified by width and height fits into the array we got */
698
-        uint64_t data_size = (uint64_t) data[0] * data[1];
699
-        if (data_size > (uint64_t) (end - data - 2)) break;
700
-
701
-        /* use the greater of the two dimensions to match against the preferred size */
702
-        uint32_t size = MAX(data[0], data[1]);
703
-
704
-        /* pick the icon if it's a better match than the one we already have */
705
-        bool found_icon_too_small = found_size < preferred_size;
706
-        bool found_icon_too_large = found_size > preferred_size;
707
-        bool icon_empty = data[0] == 0 || data[1] == 0;
708
-        bool better_because_bigger =  found_icon_too_small && size > found_size;
709
-        bool better_because_smaller = found_icon_too_large &&
710
-            size >= preferred_size && size < found_size;
711
-        if (!icon_empty && (better_because_bigger || better_because_smaller || found_size == 0))
712
-        {
713
-            found_data = data;
714
-            found_size = size;
715
-        }
681
+    uint32_t width, height;
682
+    uint64_t data_len;
683
+    uint32_t *icon_data;
716 684
 
717
-        data += data_size + 2;
718
-    }
685
+    if(data_end - *data <= 2)
686
+        return NULL;
687
+
688
+    width = (*data)[0];
689
+    height = (*data)[1];
690
+
691
+    /* Check that we have enough data, handling overflow */
692
+    data_len = width * (uint64_t) height;
693
+    if (width < 1 || height < 1 || data_len > (uint64_t) (data_end - *data) - 2)
694
+        return NULL;
695
+
696
+    icon_data = *data + 2;
697
+    *data += 2 + data_len;
698
+    return draw_surface_from_data(width, height, icon_data);
699
+}
700
+
701
+static cairo_surface_array_t
702
+ewmh_window_icon_from_reply(xcb_get_property_reply_t *r)
703
+{
704
+    uint32_t *data, *data_end;
705
+    cairo_surface_array_t result;
706
+    cairo_surface_t *s;
719 707
 
720
-    if (!found_data) return 0;
708
+    cairo_surface_array_init(&result);
709
+    if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32)
710
+        return result;
711
+
712
+    data = (uint32_t*) xcb_get_property_value(r);
713
+    data_end = &data[r->length];
714
+    if(!data)
715
+        return result;
716
+
717
+    while ((s = ewmh_window_icon_from_reply_next(&data, data_end)) != NULL) {
718
+        cairo_surface_array_push(&result, s);
719
+    }
721 720
 
722
-    return draw_surface_from_data(found_data[0], found_data[1], found_data + 2);
721
+    return result;
723 722
 }
724 723
 
725 724
 /** Get NET_WM_ICON.
726 725
  * \param cookie The cookie.
727
- * \return The number of elements on stack.
726
+ * \return An array of icons.
728 727
  */
729
-cairo_surface_t *
730
-ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie, uint32_t preferred_size)
728
+cairo_surface_array_t
729
+ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
731 730
 {
732 731
     xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
733
-    cairo_surface_t *surface = ewmh_window_icon_from_reply(r, preferred_size);
732
+    cairo_surface_array_t result = ewmh_window_icon_from_reply(r);
734 733
     p_delete(&r);
735
-    return surface;
734
+    return result;
736 735
 }
737 736
 
738 737
 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

+ 2
- 1
ewmh.h View File

@@ -28,6 +28,7 @@
28 28
 #include "strut.h"
29 29
 
30 30
 typedef struct client_t client_t;
31
+typedef struct cairo_surface_array_t cairo_surface_array_t;
31 32
 
32 33
 void ewmh_init(void);
33 34
 void ewmh_init_lua(void);
@@ -42,7 +43,7 @@ void ewmh_process_client_strut(client_t *);
42 43
 void ewmh_update_strut(xcb_window_t, strut_t *);
43 44
 void ewmh_update_window_type(xcb_window_t window, uint32_t type);
44 45
 xcb_get_property_cookie_t ewmh_window_icon_get_unchecked(xcb_window_t);
45
-cairo_surface_t *ewmh_window_icon_get_reply(xcb_get_property_cookie_t, uint32_t preferred_size);
46
+cairo_surface_array_t ewmh_window_icon_get_reply(xcb_get_property_cookie_t);
46 47
 
47 48
 #endif
48 49
 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

+ 53
- 15
objects/client.c View File

@@ -826,6 +826,7 @@ client_wipe(client_t *c)
826 826
 {
827 827
     key_array_wipe(&c->keys);
828 828
     xcb_icccm_get_wm_protocols_reply_wipe(&c->protocols);
829
+    cairo_surface_array_wipe(&c->icons);
829 830
     p_delete(&c->machine);
830 831
     p_delete(&c->class);
831 832
     p_delete(&c->instance);
@@ -834,8 +835,6 @@ client_wipe(client_t *c)
834 835
     p_delete(&c->name);
835 836
     p_delete(&c->alt_name);
836 837
     p_delete(&c->startup_id);
837
-    if(c->icon)
838
-        cairo_surface_destroy(c->icon);
839 838
 }
840 839
 
841 840
 /** Change the clients urgency flag.
@@ -2319,27 +2318,38 @@ luaA_client_isvisible(lua_State *L)
2319 2318
     return 1;
2320 2319
 }
2321 2320
 
2322
-/** Set a client icon.
2321
+/** Set client icons.
2323 2322
  * \param L The Lua VM state.
2324
- * \param cidx The client index on the stack.
2325
- * \param iidx The image index on the stack.
2323
+ * \param array Array of icons to set.
2326 2324
  */
2327 2325
 void
2328
-client_set_icon(client_t *c, cairo_surface_t *s)
2326
+client_set_icons(client_t *c, cairo_surface_array_t array)
2329 2327
 {
2330
-    lua_State *L = globalconf_get_lua_State();
2331
-
2332
-    if (s)
2333
-        s = draw_dup_image_surface(s);
2334
-    if(c->icon)
2335
-        cairo_surface_destroy(c->icon);
2336
-    c->icon = s;
2328
+    cairo_surface_array_wipe(&c->icons);
2329
+    c->icons = array;
2337 2330
 
2331
+    lua_State *L = globalconf_get_lua_State();
2338 2332
     luaA_object_push(L, c);
2339 2333
     luaA_object_emit_signal(L, -1, "property::icon", 0);
2340 2334
     lua_pop(L, 1);
2341 2335
 }
2342 2336
 
2337
+/** Set a client icon.
2338
+ * \param L The Lua VM state.
2339
+ * \param cidx The client index on the stack.
2340
+ * \param iidx The image index on the stack.
2341
+ */
2342
+static void
2343
+client_set_icon(client_t *c, cairo_surface_t *s)
2344
+{
2345
+    cairo_surface_array_t array;
2346
+    cairo_surface_array_init(&array);
2347
+    if (s && cairo_surface_status(s) == CAIRO_STATUS_SUCCESS)
2348
+        cairo_surface_array_push(&array, draw_dup_image_surface(s));
2349
+    client_set_icons(c, array);
2350
+}
2351
+
2352
+
2343 2353
 /** Set a client icon.
2344 2354
  * \param c The client to change.
2345 2355
  * \param icon A bitmap containing the icon.
@@ -3079,10 +3089,38 @@ luaA_client_get_content(lua_State *L, client_t *c)
3079 3089
 static int
3080 3090
 luaA_client_get_icon(lua_State *L, client_t *c)
3081 3091
 {
3082
-    if(!c->icon)
3092
+    if(c->icons.len == 0)
3083 3093
         return 0;
3094
+
3095
+    /* Pick the closest available size, only picking a smaller icon if no bigger
3096
+     * one is available.
3097
+     */
3098
+    cairo_surface_t *found = NULL;
3099
+    int found_size = 0;
3100
+    int preferred_size = globalconf.preferred_icon_size;
3101
+
3102
+    foreach(surf, c->icons)
3103
+    {
3104
+        int width = cairo_image_surface_get_width(*surf);
3105
+        int height = cairo_image_surface_get_height(*surf);
3106
+        int size = MAX(width, height);
3107
+
3108
+        /* pick the icon if it's a better match than the one we already have */
3109
+        bool found_icon_too_small = found_size < preferred_size;
3110
+        bool found_icon_too_large = found_size > preferred_size;
3111
+        bool icon_empty = width == 0 || height == 0;
3112
+        bool better_because_bigger =  found_icon_too_small && size > found_size;
3113
+        bool better_because_smaller = found_icon_too_large &&
3114
+            size >= preferred_size && size < found_size;
3115
+        if (!icon_empty && (better_because_bigger || better_because_smaller || found_size == 0))
3116
+        {
3117
+            found = *surf;
3118
+            found_size = size;
3119
+        }
3120
+    }
3121
+
3084 3122
     /* lua gets its own reference which it will have to destroy */
3085
-    lua_pushlightuserdata(L, cairo_surface_reference(c->icon));
3123
+    lua_pushlightuserdata(L, cairo_surface_reference(found));
3086 3124
     return 1;
3087 3125
 }
3088 3126
 

+ 3
- 3
objects/client.h View File

@@ -116,8 +116,8 @@ struct client_t
116 116
     xcb_icccm_get_wm_protocols_reply_t protocols;
117 117
     /** Key bindings */
118 118
     key_array_t keys;
119
-    /** Icon */
120
-    cairo_surface_t *icon;
119
+    /** Icons */
120
+    cairo_surface_array_t icons;
121 121
     /** True if we ever got an icon from _NET_WM_ICON */
122 122
     bool have_ewmh_icon;
123 123
     /** Size hints */
@@ -186,7 +186,7 @@ void client_set_transient_for(lua_State *L, int, client_t *);
186 186
 void client_set_name(lua_State *L, int, char *);
187 187
 void client_set_alt_name(lua_State *L, int, char *);
188 188
 void client_set_group_window(lua_State *, int, xcb_window_t);
189
-void client_set_icon(client_t *, cairo_surface_t *);
189
+void client_set_icons(client_t *, cairo_surface_array_t);
190 190
 void client_set_icon_from_pixmaps(client_t *, xcb_pixmap_t, xcb_pixmap_t);
191 191
 void client_set_skip_taskbar(lua_State *, int, bool);
192 192
 void client_focus(client_t *);

+ 6
- 8
property.c View File

@@ -206,8 +206,6 @@ property_update_wm_hints(client_t *c, xcb_get_property_cookie_t cookie)
206 206
             else
207 207
                 client_set_icon_from_pixmaps(c, wmh.icon_pixmap, XCB_NONE);
208 208
         }
209
-        else
210
-            client_set_icon(c, NULL);
211 209
     }
212 210
 
213 211
     lua_pop(L, 1);
@@ -262,14 +260,14 @@ property_get_net_wm_icon(client_t *c)
262 260
 void
263 261
 property_update_net_wm_icon(client_t *c, xcb_get_property_cookie_t cookie)
264 262
 {
265
-    cairo_surface_t *surface = ewmh_window_icon_get_reply(cookie, globalconf.preferred_icon_size);
266
-
267
-    if(!surface)
263
+    cairo_surface_array_t array = ewmh_window_icon_get_reply(cookie);
264
+    if (array.len == 0)
265
+    {
266
+        cairo_surface_array_wipe(&array);
268 267
         return;
269
-
268
+    }
270 269
     c->have_ewmh_icon = true;
271
-    client_set_icon(c, surface);
272
-    cairo_surface_destroy(surface);
270
+    client_set_icons(c, array);
273 271
 }
274 272
 
275 273
 xcb_get_property_cookie_t

Loading…
Cancel
Save