Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,18 @@ struct lab_data_buffer *buffer_create_cairo(uint32_t logical_width,
struct lab_data_buffer *buffer_create_from_data(void *pixel_data, uint32_t width,
uint32_t height, uint32_t stride);

/*
* Create a lab_data_buffer from a wlr_buffer by copying its content.
* The wlr_buffer must be backed by shm.
*/
struct lab_data_buffer *buffer_create_from_wlr_buffer(
struct wlr_buffer *wlr_buffer);

/*
* Resize a buffer to the given size. The source buffer is rendered at the
* center of the output buffer and shrunk if it overflows from the output buffer.
*/
struct lab_data_buffer *buffer_resize(struct lab_data_buffer *src_buffer,
int width, int height, double scale);

#endif /* LABWC_BUFFER_H */
17 changes: 14 additions & 3 deletions include/common/scaled-icon-buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define LABWC_SCALED_ICON_BUFFER_H

#include <stdbool.h>
#include <wayland-server-core.h>

struct wlr_scene_tree;
struct wlr_scene_node;
Expand All @@ -12,8 +13,18 @@ struct scaled_icon_buffer {
struct scaled_scene_buffer *scaled_buffer;
struct wlr_scene_buffer *scene_buffer;
struct server *server;
char *app_id;
/* for window icon */
struct view *view;
char *view_app_id;
char *view_icon_name;
struct wl_array view_icon_buffers;
struct {
struct wl_listener set_icon;
struct wl_listener destroy;
} on_view;
/* for general icon (e.g. in menus) */
char *icon_name;

int width;
int height;
};
Expand All @@ -28,8 +39,8 @@ struct scaled_icon_buffer *scaled_icon_buffer_create(
struct wlr_scene_tree *parent, struct server *server,
int width, int height);

void scaled_icon_buffer_set_app_id(struct scaled_icon_buffer *self,
const char *app_id);
void scaled_icon_buffer_set_view(struct scaled_icon_buffer *self,
struct view *view);

void scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self,
const char *icon_name);
Expand Down
3 changes: 3 additions & 0 deletions include/labwc.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ struct server {
struct wl_listener xdg_activation_request;
struct wl_listener xdg_activation_new_token;

struct wlr_xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager;
struct wl_listener xdg_toplevel_icon_set_icon;

struct wl_list views;
struct wl_list unmanaged_surfaces;

Expand Down
1 change: 0 additions & 1 deletion include/ssd.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ void ssd_update_title(struct ssd *ssd);
void ssd_update_geometry(struct ssd *ssd);
void ssd_destroy(struct ssd *ssd);
void ssd_set_titlebar(struct ssd *ssd, bool enabled);
void ssd_update_window_icon(struct ssd *ssd);

void ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable);
void ssd_enable_shade(struct ssd *ssd, bool enable);
Expand Down
16 changes: 16 additions & 0 deletions include/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ struct view {

struct foreign_toplevel *foreign_toplevel;

/* used by scaled_icon_buffer */
struct {
char *name;
struct wl_array buffers; /* struct lab_data_buffer * */
} icon;

struct {
struct wl_signal new_app_id;
struct wl_signal new_title;
Expand All @@ -289,6 +295,12 @@ struct view {
struct wl_signal minimized;
struct wl_signal fullscreened;
struct wl_signal activated; /* bool *activated */
/*
* This is emitted when app_id, or icon set via xdg_toplevel_icon
* is updated. This is listened by scaled_icon_buffer.
*/
struct wl_signal set_icon;
struct wl_signal destroy;
} events;
};

Expand Down Expand Up @@ -612,6 +624,10 @@ int view_get_min_width(void);

void view_set_shade(struct view *view, bool shaded);

/* Icon buffers set with this function are dropped later */
void view_set_icon(struct view *view, const char *icon_name,
struct wl_array *buffers);

struct view_size_hints view_get_size_hints(struct view *view);
void view_adjust_size(struct view *view, int *w, int *h);

Expand Down
62 changes: 62 additions & 0 deletions src/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
#include <stdlib.h>
#include <drm_fourcc.h>
#include <wlr/interfaces/wlr_buffer.h>
#include <wlr/util/log.h>
#include "buffer.h"
#include "common/box.h"
#include "common/mem.h"

static const struct wlr_buffer_impl data_buffer_impl;
Expand Down Expand Up @@ -146,3 +148,63 @@ buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height,
buffer->surface_owns_data = false;
return buffer;
}

struct lab_data_buffer *
buffer_create_from_wlr_buffer(struct wlr_buffer *wlr_buffer)
{
void *data;
uint32_t format;
size_t stride;
if (!wlr_buffer_begin_data_ptr_access(wlr_buffer,
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
wlr_log(WLR_ERROR, "failed to access wlr_buffer");
return NULL;
}
if (format != DRM_FORMAT_ARGB8888) {
/* TODO: support other formats */
wlr_buffer_end_data_ptr_access(wlr_buffer);
wlr_log(WLR_ERROR, "cannot create buffer: format=%d", format);
return NULL;
}
size_t buffer_size = stride * wlr_buffer->height;
void *copied_data = xmalloc(buffer_size);
memcpy(copied_data, data, buffer_size);
wlr_buffer_end_data_ptr_access(wlr_buffer);

return buffer_create_from_data(copied_data,
wlr_buffer->width, wlr_buffer->height, stride);
}

struct lab_data_buffer *
buffer_resize(struct lab_data_buffer *src_buffer, int width, int height,
double scale)
{
assert(src_buffer);
cairo_surface_t *surface = src_buffer->surface;

int src_w = cairo_image_surface_get_width(surface);
int src_h = cairo_image_surface_get_height(surface);

struct lab_data_buffer *buffer =
buffer_create_cairo(width, height, scale);
cairo_t *cairo = cairo_create(buffer->surface);

struct wlr_box container = {
.width = width,
.height = height,
};

struct wlr_box dst_box = box_fit_within(src_w, src_h, &container);
double scene_scale = (double)dst_box.width / (double)src_w;
cairo_translate(cairo, dst_box.x, dst_box.y);
cairo_scale(cairo, scene_scale, scene_scale);
cairo_set_source_surface(cairo, surface, 0, 0);
cairo_pattern_set_filter(cairo_get_source(cairo), CAIRO_FILTER_GOOD);
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
cairo_paint(cairo);

cairo_surface_flush(buffer->surface);
cairo_destroy(cairo);

return buffer;
}
148 changes: 137 additions & 11 deletions src/common/scaled-icon-buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <string.h>
#include <wlr/util/log.h>
#include "buffer.h"
#include "common/macros.h"
#include "common/mem.h"
#include "common/scaled-icon-buffer.h"
Expand All @@ -12,6 +14,35 @@
#include "desktop-entry.h"
#include "img/img.h"
#include "node.h"
#include "view.h"

#if HAVE_LIBSFDO

static struct lab_data_buffer *
choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double scale)
{
int best_dist = INT_MIN;
struct lab_data_buffer *best_buffer = NULL;

struct lab_data_buffer **buffer;
wl_array_for_each(buffer, &self->view_icon_buffers) {
int curr_dist = (*buffer)->base.width - (int)(icon_size * scale);
bool curr_is_better;
if ((curr_dist < 0 && best_dist > 0)
|| (curr_dist > 0 && best_dist < 0)) {
/* prefer too big icon over too small icon */
curr_is_better = curr_dist > 0;
} else {
curr_is_better = abs(curr_dist) < abs(best_dist);
}
if (curr_is_better) {
best_dist = curr_dist;
best_buffer = *buffer;
}
}
return best_buffer;
}
#endif /* HAVE_LIBSFDO */

static struct lab_data_buffer *
_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale)
Expand All @@ -24,10 +55,29 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale)
if (self->icon_name) {
img = desktop_entry_load_icon(self->server,
self->icon_name, icon_size, scale);
} else if (self->app_id) {
img = desktop_entry_load_icon_from_app_id(self->server,
self->app_id, icon_size, scale);
} else if (self->view) {
if (self->view_icon_name) {
wlr_log(WLR_DEBUG, "loading icon by name: %s",
self->view_icon_name);
img = desktop_entry_load_icon(self->server,
self->view_icon_name, icon_size, scale);
}
if (!img) {
struct lab_data_buffer *buffer =
choose_best_icon_buffer(self, icon_size, scale);
if (buffer) {
wlr_log(WLR_DEBUG, "loading icon by buffer");
return buffer_resize(buffer,
self->width, self->height, scale);
}
}
if (!img) {
wlr_log(WLR_DEBUG, "loading icon by app_id");
img = desktop_entry_load_icon_from_app_id(self->server,
self->view_app_id, icon_size, scale);
}
if (!img) {
wlr_log(WLR_DEBUG, "loading fallback icon");
img = desktop_entry_load_icon(self->server,
rc.fallback_app_icon_name, icon_size, scale);
}
Expand All @@ -44,26 +94,63 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale)
return buffer;
#else
return NULL;
#endif
#endif /* HAVE_LIBSFDO */
}

static void
set_icon_buffers(struct scaled_icon_buffer *self, struct wl_array *buffers)
{
struct lab_data_buffer **icon_buffer;
wl_array_for_each(icon_buffer, &self->view_icon_buffers) {
wlr_buffer_unlock(&(*icon_buffer)->base);
}
wl_array_release(&self->view_icon_buffers);
wl_array_init(&self->view_icon_buffers);

if (!buffers) {
return;
}

wl_array_for_each(icon_buffer, buffers) {
wlr_buffer_lock(&(*icon_buffer)->base);
}
wl_array_copy(&self->view_icon_buffers, buffers);
}

static void
_destroy(struct scaled_scene_buffer *scaled_buffer)
{
struct scaled_icon_buffer *self = scaled_buffer->data;
free(self->app_id);
if (self->view) {
wl_list_remove(&self->on_view.set_icon.link);
wl_list_remove(&self->on_view.destroy.link);
}
free(self->view_app_id);
free(self->view_icon_name);
set_icon_buffers(self, NULL);
free(self->icon_name);
free(self);
}

static bool
icon_buffers_equal(struct wl_array *a, struct wl_array *b)
{
if (a->size != b->size) {
return false;
}
return a->size == 0 || !memcmp(a->data, b->data, a->size);
}

static bool
_equal(struct scaled_scene_buffer *scaled_buffer_a,
struct scaled_scene_buffer *scaled_buffer_b)
{
struct scaled_icon_buffer *a = scaled_buffer_a->data;
struct scaled_icon_buffer *b = scaled_buffer_b->data;

return str_equal(a->app_id, b->app_id)
return str_equal(a->view_app_id, b->view_app_id)
&& str_equal(a->view_icon_name, b->view_icon_name)
&& icon_buffers_equal(&a->view_icon_buffers, &b->view_icon_buffers)
&& str_equal(a->icon_name, b->icon_name)
&& a->width == b->width
&& a->height == b->height;
Expand Down Expand Up @@ -96,15 +183,54 @@ scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server,
return self;
}

static void
handle_view_set_icon(struct wl_listener *listener, void *data)
{
struct scaled_icon_buffer *self =
wl_container_of(listener, self, on_view.set_icon);

/* view_get_string_prop() never returns NULL */
xstrdup_replace(self->view_app_id, view_get_string_prop(self->view, "app_id"));
zfree(self->view_icon_name);
if (self->view->icon.name) {
self->view_icon_name = xstrdup(self->view->icon.name);
}
set_icon_buffers(self, &self->view->icon.buffers);

scaled_scene_buffer_request_update(self->scaled_buffer,
self->width, self->height);
}

static void
handle_view_destroy(struct wl_listener *listener, void *data)
{
struct scaled_icon_buffer *self =
wl_container_of(listener, self, on_view.destroy);
wl_list_remove(&self->on_view.destroy.link);
wl_list_remove(&self->on_view.set_icon.link);
self->view = NULL;
}

void
scaled_icon_buffer_set_app_id(struct scaled_icon_buffer *self,
const char *app_id)
scaled_icon_buffer_set_view(struct scaled_icon_buffer *self, struct view *view)
{
assert(app_id);
if (str_equal(self->app_id, app_id)) {
assert(view);
if (self->view == view) {
return;
}
xstrdup_replace(self->app_id, app_id);

if (self->view) {
wl_list_remove(&self->on_view.set_icon.link);
wl_list_remove(&self->on_view.destroy.link);
}
self->view = view;
if (view) {
self->on_view.set_icon.notify = handle_view_set_icon;
wl_signal_add(&view->events.set_icon, &self->on_view.set_icon);
self->on_view.destroy.notify = handle_view_destroy;
wl_signal_add(&view->events.destroy, &self->on_view.destroy);
}

scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height);
}

Expand Down
Loading