From: Emmanuel Gil Peyrot <linkma...@linkmauve.fr> This fetches the _NET_WM_ICON property of the X11 window, and use the first image found as the frame icon.
This has been tested with various X11 programs, and improves usability and user-friendliness a bit. Changes since v1: - Changed frame_button_create() to use frame_button_create_from_surface() internally. - Removed a check that should never have been commited. Changes since v2: - Request UINT32_MAX items instead of 2048, to avoid cutting valid icons. - Strengthen checks against malformed input. - Handle XCB_PROPERTY_DELETE to remove the icon. - Schedule a repaint if the icon changed. Signed-off-by: Emmanuel Gil Peyrot <linkma...@linkmauve.fr> Signed-off-by: Emmanuel Gil Peyrot <emmanuel.pey...@collabora.com> --- clients/window.c | 4 +-- libweston/compositor-wayland.c | 2 +- shared/cairo-util.h | 2 +- shared/frame.c | 54 +++++++++++++++++++++++++++---------- xwayland/window-manager.c | 61 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 103 insertions(+), 20 deletions(-) diff --git a/clients/window.c b/clients/window.c index 95796d46..15a86e15 100644 --- a/clients/window.c +++ b/clients/window.c @@ -2546,7 +2546,7 @@ window_frame_create(struct window *window, void *data) frame = xzalloc(sizeof *frame); frame->frame = frame_create(window->display->theme, 0, 0, - buttons, window->title); + buttons, window->title, NULL); frame->widget = window_add_widget(window, frame); frame->child = widget_add_widget(frame->widget, data); @@ -5449,7 +5449,7 @@ create_menu(struct display *display, menu->user_data = user_data; menu->widget = window_add_widget(menu->window, menu); menu->frame = frame_create(window->display->theme, 0, 0, - FRAME_BUTTON_NONE, NULL); + FRAME_BUTTON_NONE, NULL, NULL); fail_on_null(menu->frame, 0, __FILE__, __LINE__); menu->entries = entries; menu->count = count; diff --git a/libweston/compositor-wayland.c b/libweston/compositor-wayland.c index 14f2c8db..aa6e5043 100644 --- a/libweston/compositor-wayland.c +++ b/libweston/compositor-wayland.c @@ -862,7 +862,7 @@ wayland_output_set_windowed(struct wayland_output *output) return -1; } output->frame = frame_create(b->theme, 100, 100, - FRAME_BUTTON_CLOSE, output->title); + FRAME_BUTTON_CLOSE, output->title, NULL); if (!output->frame) return -1; diff --git a/shared/cairo-util.h b/shared/cairo-util.h index 84cf005e..7893ca24 100644 --- a/shared/cairo-util.h +++ b/shared/cairo-util.h @@ -126,7 +126,7 @@ enum { struct frame * frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons, - const char *title); + const char *title, cairo_surface_t *icon); void frame_destroy(struct frame *frame); diff --git a/shared/frame.c b/shared/frame.c index eb0cd77a..32779856 100644 --- a/shared/frame.c +++ b/shared/frame.c @@ -106,9 +106,9 @@ struct frame { }; static struct frame_button * -frame_button_create(struct frame *frame, const char *icon, - enum frame_status status_effect, - enum frame_button_flags flags) +frame_button_create_from_surface(struct frame *frame, cairo_surface_t *icon, + enum frame_status status_effect, + enum frame_button_flags flags) { struct frame_button *button; @@ -116,12 +116,7 @@ frame_button_create(struct frame *frame, const char *icon, if (!button) return NULL; - button->icon = cairo_image_surface_create_from_png(icon); - if (!button->icon) { - free(button); - return NULL; - } - + button->icon = icon; button->frame = frame; button->flags = flags; button->status_effect = status_effect; @@ -131,6 +126,30 @@ frame_button_create(struct frame *frame, const char *icon, return button; } +static struct frame_button * +frame_button_create(struct frame *frame, const char *icon_name, + enum frame_status status_effect, + enum frame_button_flags flags) +{ + struct frame_button *button; + cairo_surface_t *icon; + + icon = cairo_image_surface_create_from_png(icon_name); + if (cairo_surface_status(icon) != CAIRO_STATUS_SUCCESS) + goto error; + + button = frame_button_create_from_surface(frame, icon, status_effect, + flags); + if (!button) + goto error; + + return button; + +error: + cairo_surface_destroy(icon); + return NULL; +} + static void frame_button_destroy(struct frame_button *button) { @@ -303,7 +322,7 @@ frame_destroy(struct frame *frame) struct frame * frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons, - const char *title) + const char *title, cairo_surface_t *icon) { struct frame *frame; struct frame_button *button; @@ -330,10 +349,17 @@ frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons, } if (title) { - button = frame_button_create(frame, - DATADIR "/weston/icon_window.png", - FRAME_STATUS_MENU, - FRAME_BUTTON_CLICK_DOWN); + if (icon) { + button = frame_button_create_from_surface(frame, + icon, + FRAME_STATUS_MENU, + FRAME_BUTTON_CLICK_DOWN); + } else { + button = frame_button_create(frame, + DATADIR "/weston/icon_window.png", + FRAME_STATUS_MENU, + FRAME_BUTTON_CLICK_DOWN); + } if (!button) goto free_frame; } diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index d40d56ea..e10db47c 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -138,6 +138,8 @@ struct weston_wm_window { xcb_window_t frame_id; struct frame *frame; cairo_surface_t *cairo_surface; + int icon; + cairo_surface_t *icon_surface; uint32_t surface_id; struct weston_surface *surface; struct weston_desktop_xwayland_surface *shsurf; @@ -471,6 +473,7 @@ weston_wm_window_read_properties(struct weston_wm_window *window) { wm->atom.net_wm_state, TYPE_NET_WM_STATE, NULL }, { wm->atom.net_wm_window_type, XCB_ATOM_ATOM, F(type) }, { wm->atom.net_wm_name, XCB_ATOM_STRING, F(name) }, + { wm->atom.net_wm_icon, XCB_ATOM_CARDINAL, F(icon) }, { wm->atom.net_wm_pid, XCB_ATOM_CARDINAL, F(pid) }, { wm->atom.motif_wm_hints, TYPE_MOTIF_WM_HINTS, NULL }, { wm->atom.wm_client_machine, XCB_ATOM_WM_CLIENT_MACHINE, F(machine) }, @@ -931,8 +934,9 @@ weston_wm_window_create_frame(struct weston_wm_window *window) buttons |= FRAME_BUTTON_MAXIMIZE; window->frame = frame_create(window->wm->theme, - window->width, window->height, - buttons, window->name); + window->width, window->height, + buttons, window->name, + window->icon_surface); frame_resize_inside(window->frame, window->width, window->height); weston_wm_window_get_frame_size(window, &width, &height); @@ -1271,6 +1275,49 @@ weston_wm_window_schedule_repaint(struct weston_wm_window *window) } static void +weston_wm_handle_icon(struct weston_wm *wm, struct weston_wm_window *window) +{ + xcb_get_property_reply_t *reply; + xcb_get_property_cookie_t cookie; + int length; + unsigned *data, width, height; + + /* TODO: icons don’t have any specified order, we should pick the + * closest one to the target dimension instead of the first one. */ + + cookie = xcb_get_property(wm->conn, 0, window->id, + wm->atom.net_wm_icon, XCB_ATOM_ANY, 0, UINT32_MAX); + reply = xcb_get_property_reply(wm->conn, cookie, NULL); + length = xcb_get_property_value_length(reply); + + /* This is in 32-bit words, not in bytes. */ + if (length < 2) + return; + + data = xcb_get_property_value(reply); + width = *data++; + height = *data++; + + /* Some checks against malformed input. */ + if (width == 0 || height == 0 || (unsigned)length < 2 + width * height) + return; + + if (window->icon_surface) + cairo_surface_destroy(window->icon_surface); + + window->icon_surface = + cairo_image_surface_create_for_data((unsigned char *)data, + CAIRO_FORMAT_ARGB32, + width, height, width * 4); + + /* Bail out in case anything wrong happened during surface creation. */ + if (cairo_surface_status(window->icon_surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(window->icon_surface); + window->icon_surface = NULL; + } +} + +static void weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event) { xcb_property_notify_event_t *property_notify = @@ -1290,6 +1337,16 @@ weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *even read_and_dump_property(wm, property_notify->window, property_notify->atom); + if (property_notify->atom == wm->atom.net_wm_icon) { + if (property_notify->state != XCB_PROPERTY_DELETE) { + weston_wm_handle_icon(wm, window); + } else { + cairo_surface_destroy(window->icon_surface); + window->icon_surface = NULL; + } + weston_wm_window_schedule_repaint(window); + } + if (property_notify->atom == wm->atom.net_wm_name || property_notify->atom == XCB_ATOM_WM_NAME) weston_wm_window_schedule_repaint(window); -- 2.12.2 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/wayland-devel