From 6b58ea8c43ac81e519bd418efbf24687a5d731b8 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Fri, 1 Dec 2017 19:20:40 +0100 Subject: [PATCH] xwm: Add icon support to the frame 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. Changes since v3: - Keep the previous Cairo surface until the new one has been successfully loaded. - Use uint32_t for cardinals. Unsigned is the same type except on 16-bit machines, but uint32_t is clearer. - Declare length as uint32_t too, like in xcb_get_property_reply_t. Signed-off-by: Emmanuel Gil Peyrot Reviewed-by: Quentin Glidic --- clients/window.c | 4 +-- libweston/compositor-wayland.c | 2 +- shared/cairo-util.h | 2 +- shared/frame.c | 54 ++++++++++++++++++++-------- xwayland/window-manager.c | 65 ++++++++++++++++++++++++++++++++-- 5 files changed, 107 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 3bdfb03e..c5290d85 100644 --- a/libweston/compositor-wayland.c +++ b/libweston/compositor-wayland.c @@ -869,7 +869,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 9481e58c..bab48083 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 5ca7e08b..dc7ff85c 100644 --- a/shared/frame.c +++ b/shared/frame.c @@ -108,9 +108,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; @@ -118,12 +118,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; @@ -133,6 +128,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) { @@ -305,7 +324,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; @@ -332,10 +351,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 be895e7a..ac44a29a 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; @@ -473,6 +475,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) }, @@ -988,8 +991,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); @@ -1347,6 +1351,53 @@ weston_wm_window_schedule_repaint(struct weston_wm_window *window) weston_wm_window_do_repaint, 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; + uint32_t length; + uint32_t *data, width, height; + cairo_surface_t *new_surface; + + /* 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 || length < 2 + width * height) + return; + + new_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(new_surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(new_surface); + return; + } + + if (window->icon_surface) + cairo_surface_destroy(window->icon_surface); + + window->icon_surface = new_surface; +} + static void weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *event) { @@ -1367,6 +1418,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);