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 <linkmauve@linkmauve.fr>
Reviewed-by: Quentin Glidic <sardemff7+git@sardemff7.net>
This commit is contained in:
Emmanuel Gil Peyrot
2017-12-01 19:20:40 +01:00
committed by Daniel Stone
parent cb04cc4f68
commit 6b58ea8c43
5 changed files with 107 additions and 20 deletions
+63 -2
View File
@@ -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 dont 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);