|
|
|
diff -Nurp ../i3-4.13/include/atoms_rest.xmacro ./include/atoms_rest.xmacro
|
|
|
|
--- ../i3-4.13/include/atoms_rest.xmacro 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./include/atoms_rest.xmacro 2016-11-13 14:14:05.086086381 +0100
|
|
|
|
@@ -1,6 +1,7 @@
|
|
|
|
xmacro(_NET_WM_USER_TIME)
|
|
|
|
xmacro(_NET_STARTUP_ID)
|
|
|
|
xmacro(_NET_WORKAREA)
|
|
|
|
+xmacro(_NET_WM_ICON)
|
|
|
|
xmacro(WM_PROTOCOLS)
|
|
|
|
xmacro(WM_DELETE_WINDOW)
|
|
|
|
xmacro(UTF8_STRING)
|
|
|
|
diff -Nurp ../i3-4.13/include/data.h ./include/data.h
|
|
|
|
--- ../i3-4.13/include/data.h 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./include/data.h 2016-11-13 14:40:42.765019561 +0100
|
|
|
|
@@ -440,6 +440,11 @@ struct Window {
|
|
|
|
|
|
|
|
/* aspect ratio from WM_NORMAL_HINTS (MPlayer uses this for example) */
|
|
|
|
double aspect_ratio;
|
|
|
|
+
|
|
|
|
+ /** Window icon, as array of ARGB pixels */
|
|
|
|
+ unsigned char *icon;
|
|
|
|
+ int icon_width;
|
|
|
|
+ int icon_height;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
diff -Nurp ../i3-4.13/include/libi3.h ./include/libi3.h
|
|
|
|
--- ../i3-4.13/include/libi3.h 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./include/libi3.h 2016-11-13 14:40:49.707019271 +0100
|
|
|
|
@@ -586,6 +586,11 @@ color_t draw_util_hex_to_color(const cha
|
|
|
|
void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width);
|
|
|
|
|
|
|
|
/**
|
|
|
|
+ * Draw the given image using libi3.
|
|
|
|
+ */
|
|
|
|
+void draw_util_image(unsigned char *src, int src_width, int src_height, surface_t *surface, int x, int y, int width, int height);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
* Draws a filled rectangle.
|
|
|
|
* This function is a convenience wrapper and takes care of flushing the
|
|
|
|
* surface as well as restoring the cairo state.
|
|
|
|
diff -Nurp ../i3-4.13/include/window.h ./include/window.h
|
|
|
|
--- ../i3-4.13/include/window.h 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./include/window.h 2016-11-13 14:14:05.088086381 +0100
|
|
|
|
@@ -89,3 +89,9 @@ void window_update_hints(i3Window *win,
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style);
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Updates the _NET_WM_ICON
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+void window_update_icon(i3Window *win, xcb_get_property_reply_t *prop);
|
|
|
|
diff -Nurp ../i3-4.13/libi3/draw_util.c ./libi3/draw_util.c
|
|
|
|
--- ../i3-4.13/libi3/draw_util.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./libi3/draw_util.c 2016-11-13 14:44:55.628008986 +0100
|
|
|
|
@@ -135,6 +135,41 @@ void draw_util_text(i3String *text, surf
|
|
|
|
cairo_surface_mark_dirty(surface->surface);
|
|
|
|
}
|
|
|
|
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Draw the given image using libi3.
|
|
|
|
+ * This function is a convenience wrapper and takes care of flushing the
|
|
|
|
+ * surface as well as restoring the cairo state.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+void draw_util_image(unsigned char *src, int src_width, int src_height, surface_t *surface, int x, int y, int width, int height) {
|
|
|
|
+ RETURN_UNLESS_SURFACE_INITIALIZED(surface);
|
|
|
|
+
|
|
|
|
+ cairo_surface_t *image;
|
|
|
|
+ double scale;
|
|
|
|
+
|
|
|
|
+ cairo_save(surface->cr);
|
|
|
|
+
|
|
|
|
+ image = cairo_image_surface_create_for_data(
|
|
|
|
+ src,
|
|
|
|
+ CAIRO_FORMAT_ARGB32,
|
|
|
|
+ src_width,
|
|
|
|
+ src_height,
|
|
|
|
+ src_width * 4);
|
|
|
|
+
|
|
|
|
+ cairo_translate(surface->cr, x, y);
|
|
|
|
+
|
|
|
|
+ scale = MIN((double)width / src_width, (double)height / src_height);
|
|
|
|
+ cairo_scale(surface->cr, scale, scale);
|
|
|
|
+
|
|
|
|
+ cairo_set_source_surface(surface->cr, image, 0, 0);
|
|
|
|
+ cairo_paint(surface->cr);
|
|
|
|
+
|
|
|
|
+ cairo_surface_destroy(image);
|
|
|
|
+
|
|
|
|
+ cairo_restore(surface->cr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
/**
|
|
|
|
* Draws a filled rectangle.
|
|
|
|
* This function is a convenience wrapper and takes care of flushing the
|
|
|
|
diff -Nurp ../i3-4.13/src/handlers.c ./src/handlers.c
|
|
|
|
--- ../i3-4.13/src/handlers.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./src/handlers.c 2016-11-13 14:36:19.002030592 +0100
|
|
|
|
@@ -1287,6 +1287,20 @@ static bool handle_strut_partial_change(
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
+static bool handle_windowicon_change(void *data, xcb_connection_t *conn, uint8_t state,
|
|
|
|
+ xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
|
|
|
|
+ Con *con;
|
|
|
|
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
|
|
|
|
+ return false;
|
|
|
|
+
|
|
|
|
+ window_update_icon(con->window, prop);
|
|
|
|
+
|
|
|
|
+ x_push_changes(croot);
|
|
|
|
+
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
/* Returns false if the event could not be processed (e.g. the window could not
|
|
|
|
* be found), true otherwise */
|
|
|
|
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
|
|
|
|
@@ -1308,7 +1322,8 @@ static struct property_handler_t propert
|
|
|
|
{0, 128, handle_class_change},
|
|
|
|
{0, UINT_MAX, handle_strut_partial_change},
|
|
|
|
{0, UINT_MAX, handle_window_type},
|
|
|
|
- {0, 5 * sizeof(uint64_t), handle_motif_hints_change}};
|
|
|
|
+ {0, 5 * sizeof(uint64_t), handle_motif_hints_change},
|
|
|
|
+ {0, UINT_MAX, handle_windowicon_change}};
|
|
|
|
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -1330,6 +1345,7 @@ void property_handlers_init(void) {
|
|
|
|
property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
|
|
|
|
property_handlers[9].atom = A__NET_WM_WINDOW_TYPE;
|
|
|
|
property_handlers[10].atom = A__MOTIF_WM_HINTS;
|
|
|
|
+ property_handlers[11].atom = A__NET_WM_ICON;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
|
|
|
|
diff -Nurp ../i3-4.13/src/manage.c ./src/manage.c
|
|
|
|
--- ../i3-4.13/src/manage.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./src/manage.c 2016-11-13 14:14:05.090086380 +0100
|
|
|
|
@@ -91,6 +91,8 @@ void manage_window(xcb_window_t window,
|
|
|
|
role_cookie, startup_id_cookie, wm_hints_cookie,
|
|
|
|
wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie;
|
|
|
|
|
|
|
|
+ xcb_get_property_cookie_t wm_icon_cookie;
|
|
|
|
+
|
|
|
|
geomc = xcb_get_geometry(conn, d);
|
|
|
|
|
|
|
|
/* Check if the window is mapped (it could be not mapped when intializing and
|
|
|
|
@@ -162,6 +164,7 @@ void manage_window(xcb_window_t window,
|
|
|
|
motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t));
|
|
|
|
wm_user_time_cookie = GET_PROPERTY(A__NET_WM_USER_TIME, UINT32_MAX);
|
|
|
|
wm_desktop_cookie = GET_PROPERTY(A__NET_WM_DESKTOP, UINT32_MAX);
|
|
|
|
+ wm_icon_cookie = GET_PROPERTY(A__NET_WM_ICON, UINT32_MAX);
|
|
|
|
|
|
|
|
DLOG("Managing window 0x%08x\n", window);
|
|
|
|
|
|
|
|
@@ -177,6 +180,7 @@ void manage_window(xcb_window_t window,
|
|
|
|
window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true);
|
|
|
|
window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true);
|
|
|
|
window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true);
|
|
|
|
+ window_update_icon(cwindow, xcb_get_property_reply(conn, wm_icon_cookie, NULL));
|
|
|
|
window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL));
|
|
|
|
window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
|
|
|
|
window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
|
|
|
|
@@ -185,6 +189,8 @@ void manage_window(xcb_window_t window,
|
|
|
|
window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint);
|
|
|
|
border_style_t motif_border_style = BS_NORMAL;
|
|
|
|
window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
xcb_size_hints_t wm_size_hints;
|
|
|
|
if (!xcb_icccm_get_wm_size_hints_reply(conn, wm_normal_hints_cookie, &wm_size_hints, NULL))
|
|
|
|
memset(&wm_size_hints, '\0', sizeof(xcb_size_hints_t));
|
|
|
|
diff -Nurp ../i3-4.13/src/render.c ./src/render.c
|
|
|
|
--- ../i3-4.13/src/render.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./src/render.c 2016-11-13 14:26:45.732054568 +0100
|
|
|
|
@@ -125,6 +125,10 @@ void render_con(Con *con, bool render_fu
|
|
|
|
/* find the height for the decorations */
|
|
|
|
params.deco_height = render_deco_height();
|
|
|
|
|
|
|
|
+ /* minimum decoration height to allow icon to fit
|
|
|
|
+ * not actually required, icon would be cropped otherwise */
|
|
|
|
+ params.deco_height = (params.deco_height < 16) ? 16 : params.deco_height;
|
|
|
|
+
|
|
|
|
/* precalculate the sizes to be able to correct rounding errors */
|
|
|
|
params.sizes = precalculate_sizes(con, ¶ms);
|
|
|
|
|
|
|
|
diff -Nurp ../i3-4.13/src/window.c ./src/window.c
|
|
|
|
--- ../i3-4.13/src/window.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./src/window.c 2016-11-13 14:48:01.177001226 +0100
|
|
|
|
@@ -17,6 +17,7 @@ void window_free(i3Window *win) {
|
|
|
|
FREE(win->class_class);
|
|
|
|
FREE(win->class_instance);
|
|
|
|
i3string_free(win->name);
|
|
|
|
+ FREE(win->icon);
|
|
|
|
FREE(win->ran_assignments);
|
|
|
|
FREE(win);
|
|
|
|
}
|
|
|
|
@@ -365,3 +366,62 @@ void window_update_motif_hints(i3Window
|
|
|
|
#undef MWM_DECOR_BORDER
|
|
|
|
#undef MWM_DECOR_TITLE
|
|
|
|
}
|
|
|
|
+
|
|
|
|
+void window_update_icon(i3Window *win, xcb_get_property_reply_t *prop)
|
|
|
|
+{
|
|
|
|
+ uint32_t *data = NULL;
|
|
|
|
+ uint64_t len = 0;
|
|
|
|
+
|
|
|
|
+ if (!prop || prop->type != XCB_ATOM_CARDINAL || prop->format != 32) {
|
|
|
|
+ DLOG("_NET_WM_ICON is not set\n");
|
|
|
|
+ FREE(prop);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ uint32_t prop_value_len = xcb_get_property_value_length(prop);
|
|
|
|
+ uint32_t *prop_value = (uint32_t *) xcb_get_property_value(prop);
|
|
|
|
+
|
|
|
|
+ /* Find the number of icons in the reply. */
|
|
|
|
+ while (prop_value_len > (sizeof(uint32_t) * 2) && prop_value &&
|
|
|
|
+ prop_value[0] && prop_value[1])
|
|
|
|
+ {
|
|
|
|
+ /* Check that the property is as long as it should be (in bytes),
|
|
|
|
+ handling integer overflow. "+2" to handle the width and height
|
|
|
|
+ fields. */
|
|
|
|
+ const uint64_t crt_len = prop_value[0] * (uint64_t) prop_value[1];
|
|
|
|
+ const uint64_t expected_len = (crt_len + 2) * 4;
|
|
|
|
+
|
|
|
|
+ if (expected_len > prop_value_len) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (len == 0 || (crt_len >= 16*16 && crt_len < len)) {
|
|
|
|
+ len = crt_len;
|
|
|
|
+ data = prop_value;
|
|
|
|
+ }
|
|
|
|
+ if (len == 16*16) {
|
|
|
|
+ break; // found 16 pixels icon
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Find pointer to next icon in the reply. */
|
|
|
|
+ prop_value_len -= expected_len;
|
|
|
|
+ prop_value = (uint32_t *) (((uint8_t *) prop_value) + expected_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!data) {
|
|
|
|
+ DLOG("Could not get _NET_WM_ICON\n");
|
|
|
|
+ FREE(prop);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ LOG("Got _NET_WM_ICON of size: (%d,%d)\n", data[0], data[1]);
|
|
|
|
+ win->name_x_changed = true; // trigger a redraw
|
|
|
|
+
|
|
|
|
+ win->icon_width = data[0];
|
|
|
|
+ win->icon_height = data[1];
|
|
|
|
+
|
|
|
|
+ win->icon = malloc(len * 4);
|
|
|
|
+ memcpy(win->icon, &data[2], len * 4);
|
|
|
|
+
|
|
|
|
+ FREE(prop);
|
|
|
|
+}
|
|
|
|
diff -Nurp ../i3-4.13/src/x.c ./src/x.c
|
|
|
|
--- ../i3-4.13/src/x.c 2016-11-08 19:54:01.000000000 +0100
|
|
|
|
+++ ./src/x.c 2016-11-13 14:48:26.856000152 +0100
|
|
|
|
@@ -538,6 +538,7 @@ void x_draw_decoration(Con *con) {
|
|
|
|
|
|
|
|
/* 6: draw the title */
|
|
|
|
int text_offset_y = (con->deco_rect.height - config.font.height) / 2;
|
|
|
|
+ int text_offset_x = 0;
|
|
|
|
|
|
|
|
struct Window *win = con->window;
|
|
|
|
if (win == NULL) {
|
|
|
|
@@ -567,6 +568,9 @@ void x_draw_decoration(Con *con) {
|
|
|
|
if (win->name == NULL)
|
|
|
|
goto copy_pixmaps;
|
|
|
|
|
|
|
|
+ if (win->icon)
|
|
|
|
+ text_offset_x = 18;
|
|
|
|
+
|
|
|
|
int mark_width = 0;
|
|
|
|
if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) {
|
|
|
|
char *formatted_mark = sstrdup("");
|
|
|
|
@@ -602,12 +606,30 @@ void x_draw_decoration(Con *con) {
|
|
|
|
i3String *title = con->title_format == NULL ? win->name : con_parse_title_format(con);
|
|
|
|
draw_util_text(title, &(parent->frame_buffer),
|
|
|
|
p->color->text, p->color->background,
|
|
|
|
- con->deco_rect.x + logical_px(2),
|
|
|
|
+ con->deco_rect.x + text_offset_x + logical_px(2),
|
|
|
|
con->deco_rect.y + text_offset_y,
|
|
|
|
- con->deco_rect.width - mark_width - 2 * logical_px(2));
|
|
|
|
+ con->deco_rect.width - text_offset_x - mark_width - 2 * logical_px(2));
|
|
|
|
if (con->title_format != NULL)
|
|
|
|
I3STRING_FREE(title);
|
|
|
|
|
|
|
|
+ /* Draw the icon */
|
|
|
|
+ if (win->icon) {
|
|
|
|
+ uint16_t width = 16;
|
|
|
|
+ uint16_t height = 16;
|
|
|
|
+
|
|
|
|
+ int icon_offset_y = (con->deco_rect.height - height) / 2;
|
|
|
|
+
|
|
|
|
+ draw_util_image(
|
|
|
|
+ win->icon,
|
|
|
|
+ win->icon_width,
|
|
|
|
+ win->icon_height,
|
|
|
|
+ &(parent->frame_buffer),
|
|
|
|
+ con->deco_rect.x + text_offset_x - width,
|
|
|
|
+ con->deco_rect.y + icon_offset_y,
|
|
|
|
+ width,
|
|
|
|
+ height);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
after_title:
|
|
|
|
x_draw_decoration_after_title(con, p);
|
|
|
|
copy_pixmaps:
|