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:
parent
cb04cc4f68
commit
6b58ea8c43
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user