[PATCH weston 2/9] shell: Implement panel window list

Scott Moreau oreaus at gmail.com
Thu Mar 7 20:47:11 PST 2013


This patch uses the special surface_data interface to send information about
the surface to the shell. The shell then uses this information to render a
window list in the panel.
---
 clients/desktop-shell.c |  479 +++++++++++++++++++++++++++++++++++++++++++++--
 data/Makefile.am        |    1 +
 data/list_item_icon.png |  Bin 0 -> 176 bytes
 src/compositor.c        |    3 +
 src/compositor.h        |    1 +
 src/shell.c             |  201 ++++++++++++++++++++
 6 files changed, 671 insertions(+), 14 deletions(-)
 create mode 100644 data/list_item_icon.png

diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 41e7daa..2b5f7c8 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -44,6 +44,8 @@
 
 #include "desktop-shell-client-protocol.h"
 
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
 extern char **environ; /* defined by libc */
 
 struct desktop {
@@ -51,31 +53,55 @@ struct desktop {
 	struct desktop_shell *shell;
 	struct unlock_dialog *unlock_dialog;
 	struct task unlock_task;
+	struct wl_list surfaces;
 	struct wl_list outputs;
+	uint32_t output_count;
 
 	struct window *grab_window;
 	struct widget *grab_widget;
 
 	enum cursor_type grab_cursor;
+
+	struct surface_data_manager *surface_data_manager;
 };
 
 struct surface {
+	struct surface_data *surface_data;
+	struct desktop *desktop;
+	uint32_t output_mask;
+	char *title;
+
+	/* One window list item per panel of the surface's output_mask */
+	struct wl_list item_list;
+
+	struct wl_list link;
+};
+
+struct resize {
 	void (*configure)(void *data,
 			  struct desktop_shell *desktop_shell,
 			  uint32_t edges, struct window *window,
 			  int32_t width, int32_t height);
 };
 
+struct rgba {
+	float r, g, b, a;
+};
+
 struct panel {
-	struct surface base;
+	struct resize base;
 	struct window *window;
 	struct widget *widget;
 	struct wl_list launcher_list;
+	struct wl_list window_list;
+	struct rectangle window_list_rect;
+	uint32_t surface_count;
+	struct rgba focused_item;
 	struct panel_clock *clock;
 };
 
 struct background {
-	struct surface base;
+	struct resize base;
 	struct window *window;
 	struct widget *widget;
 };
@@ -83,11 +109,22 @@ struct background {
 struct output {
 	struct wl_output *output;
 	struct wl_list link;
+	uint32_t id;
 
 	struct panel *panel;
 	struct background *background;
 };
 
+struct list_item {
+	struct surface *surface;
+	struct widget *widget;
+	struct panel *panel;
+	cairo_surface_t *icon;
+	int focused, highlight;
+	struct wl_list link;
+	struct wl_list surface_link;
+};
+
 struct panel_launcher {
 	struct widget *widget;
 	struct panel *panel;
@@ -249,6 +286,15 @@ set_hex_color(cairo_t *cr, uint32_t color)
 }
 
 static void
+get_hex_color_rgba(uint32_t color, float *r, float *g, float *b, float *a)
+{
+	*r = ((color >> 16) & 0xff) / 255.0;
+	*g = ((color >>  8) & 0xff) / 255.0;
+	*b = ((color >>  0) & 0xff) / 255.0;
+	*a = ((color >> 24) & 0xff) / 255.0;
+}
+
+static void
 panel_redraw_handler(struct widget *widget, void *data)
 {
 	cairo_surface_t *surface;
@@ -421,15 +467,46 @@ panel_button_handler(struct widget *widget,
 }
 
 static void
+panel_window_list_schedule_redraw(struct panel *panel)
+{
+	struct list_item *item;
+	int item_width, padding, x, w;
+
+	/* If there are no window list items, redraw the panel to clear it */
+	if (wl_list_empty(&panel->window_list)) {
+		widget_schedule_redraw(panel->widget);
+		return;
+	}
+
+	item_width = (panel->window_list_rect.width / panel->surface_count);
+	padding = 2;
+
+	x = panel->window_list_rect.x + padding;
+	w = MIN(item_width - padding, 200);
+
+	wl_list_for_each(item, &panel->window_list, link) {
+		widget_set_allocation(item->widget, x, 4, w, 24);
+
+		x += w + padding;
+		widget_schedule_redraw(item->widget);
+	}
+}
+
+static void
 panel_resize_handler(struct widget *widget,
 		     int32_t width, int32_t height, void *data)
 {
 	struct panel_launcher *launcher;
+	struct rectangle launcher_rect;
+	struct rectangle clock_rect;
 	struct panel *panel = data;
 	int x, y, w, h;
-	
+
 	x = 10;
 	y = 16;
+
+	launcher_rect.x = x;
+
 	wl_list_for_each(launcher, &panel->launcher_list, link) {
 		w = cairo_image_surface_get_width(launcher->icon);
 		h = cairo_image_surface_get_height(launcher->icon);
@@ -437,12 +514,25 @@ panel_resize_handler(struct widget *widget,
 				      x, y - h / 2, w + 1, h + 1);
 		x += w + 10;
 	}
-	h=20;
+
+	launcher_rect.width = x - launcher_rect.x;
+
 	w=170;
+	h=20;
 
 	if (panel->clock)
 		widget_set_allocation(panel->clock->widget,
-				      width - w - 8, y - h / 2, w + 1, h + 1);
+				      width - w - 8, 4, w, 24);
+
+	widget_get_allocation(panel->clock->widget, &clock_rect);
+
+	panel->window_list_rect.x = launcher_rect.x + launcher_rect.width;
+	panel->window_list_rect.y = 2;
+	panel->window_list_rect.width = width -
+					panel->window_list_rect.x -
+					(clock_rect.width + 20);
+	panel->window_list_rect.height = 28;
+	panel_window_list_schedule_redraw(panel);
 }
 
 static void
@@ -451,8 +541,8 @@ panel_configure(void *data,
 		uint32_t edges, struct window *window,
 		int32_t width, int32_t height)
 {
-	struct surface *surface = window_get_user_data(window);
-	struct panel *panel = container_of(surface, struct panel, base);
+	struct resize *resize = window_get_user_data(window);
+	struct panel *panel = container_of(resize, struct panel, base);
 
 	window_schedule_resize(panel->window, width, 32);
 }
@@ -490,6 +580,22 @@ panel_destroy(struct panel *panel)
 	free(panel);
 }
 
+static void
+panel_set_list_item_focus_color(struct panel *panel)
+{
+	float r, g, b, a;
+
+	/* Consider panel color when choosing item highlight color */
+	get_hex_color_rgba(key_panel_color, &r, &b, &g, &a);
+	r += 0.2;
+	g += 0.2;
+	b += 0.2;
+	panel->focused_item.r = r > 1.0 ? 0.6 : r;
+	panel->focused_item.g = g > 1.0 ? 0.6 : g;
+	panel->focused_item.b = b > 1.0 ? 0.6 : b;
+	panel->focused_item.a = 0.75;
+}
+
 static struct panel *
 panel_create(struct display *display)
 {
@@ -502,6 +608,7 @@ panel_create(struct display *display)
 	panel->window = window_create_custom(display);
 	panel->widget = window_add_widget(panel->window, panel);
 	wl_list_init(&panel->launcher_list);
+	wl_list_init(&panel->window_list);
 
 	window_set_title(panel->window, "panel");
 	window_set_user_data(panel->window, panel);
@@ -509,7 +616,9 @@ panel_create(struct display *display)
 	widget_set_redraw_handler(panel->widget, panel_redraw_handler);
 	widget_set_resize_handler(panel->widget, panel_resize_handler);
 	widget_set_button_handler(panel->widget, panel_button_handler);
-	
+
+	panel->surface_count = 0;
+	panel_set_list_item_focus_color(panel);
 	panel_add_clock(panel);
 
 	return panel;
@@ -518,18 +627,17 @@ panel_create(struct display *display)
 static cairo_surface_t *
 load_icon_or_fallback(const char *icon)
 {
-	cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);
+	cairo_surface_t *surface = load_cairo_surface(icon);
 	cairo_t *cr;
 
-	if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS)
+	if (surface)
 		return surface;
 
 	cairo_surface_destroy(surface);
 	fprintf(stderr, "ERROR loading icon from file '%s'\n", icon);
 
 	/* draw fallback icon */
-	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-					     20, 20);
+	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 20, 20);
 	cr = cairo_create(surface);
 
 	cairo_set_source_rgba(cr, 0.8, 0.8, 0.8, 1);
@@ -862,9 +970,9 @@ desktop_shell_configure(void *data,
 			int32_t width, int32_t height)
 {
 	struct window *window = wl_surface_get_user_data(surface);
-	struct surface *s = window_get_user_data(window);
+	struct resize *r = window_get_user_data(window);
 
-	s->configure(data, desktop_shell, edges, window, width, height);
+	r->configure(data, desktop_shell, edges, window, width, height);
 }
 
 static void
@@ -946,6 +1054,328 @@ background_destroy(struct background *background)
 	free(background);
 }
 
+static void
+panel_list_item_redraw_handler(struct widget *widget, void *data)
+{
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	struct list_item *item = data;
+	struct rectangle rect;
+	cairo_text_extents_t extents;
+	cairo_font_extents_t font_extents;
+	int icon_width, icon_height;
+	unsigned int dots;
+	double padding;
+	char title[128];
+
+	widget_get_allocation(widget, &rect);
+	if (rect.width == 0)
+		return;
+
+	surface = window_get_surface(item->panel->window);
+	cr = cairo_create(surface);
+
+	if (item->focused) {
+		cairo_set_source_rgba(cr, item->panel->focused_item.r,
+					  item->panel->focused_item.g,
+					  item->panel->focused_item.b,
+					  item->panel->focused_item.a);
+		cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
+		cairo_fill(cr);
+	}
+
+	icon_width = cairo_image_surface_get_width(item->icon);
+	icon_height = cairo_image_surface_get_height(item->icon);
+	padding = (rect.height / 2.f) - (icon_height / 2.f);
+	if (rect.width > icon_width * 2) {
+		cairo_set_source_surface(cr, item->icon,
+					 rect.x + padding,
+					 rect.y + padding);
+		cairo_paint(cr);
+	} else {
+		icon_width = 0;
+		icon_height = 0;
+		padding = 1;
+	}
+
+	strcpy(title, item->surface->title);
+	cairo_select_font_face(cr, "sans",
+			       CAIRO_FONT_SLANT_NORMAL,
+			       CAIRO_FONT_WEIGHT_NORMAL);
+	cairo_set_font_size(cr, 14);
+	cairo_text_extents(cr, title, &extents);
+
+	/* If the string is too long, clip text to button width */
+	while (extents.width > (rect.width - (icon_width + padding * 3))) {
+		title[strlen(title) - 1] = '\0';
+		cairo_text_extents(cr, title, &extents);
+		if (extents.width <= 0) {
+			title[0] = '\0';
+			break;
+		}
+	}
+
+	/* If the text is clipped, add an ellipsis */
+	dots = 3;
+	if (strlen(title) < dots)
+		dots = strlen(title) + 1;
+	if (strlen(title) != strlen(item->surface->title))
+		while (dots-- > 0)
+			title[strlen(title) - dots] = '.';
+
+	cairo_font_extents (cr, &font_extents);
+	cairo_move_to(cr, rect.x + icon_width + padding * 3 + 1,
+		      rect.y + 3 * (rect.height >> 2) + 1);
+	cairo_set_source_rgb(cr, 0, 0, 0);
+	cairo_show_text(cr, title);
+	cairo_move_to(cr, rect.x + icon_width + padding * 3,
+		      rect.y + 3 * (rect.height >> 2));
+	if (item->highlight)
+		cairo_set_source_rgb(cr, 1, 1, 1);
+	else
+		cairo_set_source_rgb(cr, 0.85, 0.85, 0.85);
+	cairo_show_text(cr, title);
+	cairo_destroy(cr);
+}
+
+static int
+panel_list_item_motion_handler(struct widget *widget, struct input *input,
+			      uint32_t time, float x, float y, void *data)
+{
+	struct list_item *item = data;
+
+	widget_set_tooltip(widget, basename((char *)item->surface->title), x, y);
+
+	return CURSOR_LEFT_PTR;
+}
+
+static int
+panel_list_item_enter_handler(struct widget *widget, struct input *input,
+			     float x, float y, void *data)
+{
+	struct list_item *item = data;
+
+	item->highlight = 1;
+	widget_schedule_redraw(widget);
+
+	return CURSOR_LEFT_PTR;
+}
+
+static void
+panel_list_item_leave_handler(struct widget *widget,
+			     struct input *input, void *data)
+{
+	struct list_item *item = data;
+
+	item->highlight = 0;
+	widget_destroy_tooltip(widget);
+	widget_schedule_redraw(widget);
+}
+
+static void
+panel_list_item_button_handler(struct widget *widget,
+			      struct input *input, uint32_t time,
+			      uint32_t button,
+			      enum wl_pointer_button_state state, void *data)
+{
+	widget_schedule_redraw(widget);
+	/* TODO: Toggle minimize */
+}
+
+static struct list_item *
+panel_list_item_add(struct panel *panel, const char *icon, const char *text)
+{
+	struct list_item *item;
+	item = malloc(sizeof *item);
+	memset(item, 0, sizeof *item);
+
+	item->icon = load_icon_or_fallback(icon);
+
+	item->panel = panel;
+	wl_list_insert(panel->window_list.prev, &item->link);
+	panel->surface_count++;
+
+	item->widget = widget_add_widget(panel->widget, item);
+	widget_set_enter_handler(item->widget, panel_list_item_enter_handler);
+	widget_set_leave_handler(item->widget, panel_list_item_leave_handler);
+	widget_set_button_handler(item->widget, panel_list_item_button_handler);
+	widget_set_redraw_handler(item->widget, panel_list_item_redraw_handler);
+	widget_set_motion_handler(item->widget, panel_list_item_motion_handler);
+
+	return item;
+}
+
+static void
+panel_list_item_remove(struct list_item *item)
+{
+	item->panel->surface_count--;
+	wl_list_remove(&item->link);
+	wl_list_remove(&item->surface_link);
+	widget_destroy(item->widget);
+	panel_window_list_schedule_redraw(item->panel);
+	cairo_surface_destroy(item->icon);
+	free(item);
+}
+
+static int
+panel_list_item_exists(struct panel *panel, struct surface *surface)
+{
+	struct list_item *p_item, *s_item;
+
+	wl_list_for_each(p_item, &panel->window_list, link) {
+		wl_list_for_each(s_item, &surface->item_list, surface_link) {
+			if (p_item == s_item)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void
+output_update_window_list(struct output *output, struct surface *surface)
+{
+	struct list_item *item, *next;
+	struct panel *panel;
+
+	panel = output->panel;
+
+	/* Make a list item for each panel of the surfaces output mask */
+	if ((1 << output->id) & surface->output_mask) {
+		if (!panel_list_item_exists(panel, surface)) {
+			item = panel_list_item_add(panel,
+					DATADIR "/weston/list_item_icon.png",
+					surface->title);
+			wl_list_insert(surface->item_list.prev,
+							&item->surface_link);
+			item->surface = surface;
+		}
+	} else {
+		/* Remove item from panel if surface
+		 * is no longer on the output */
+		wl_list_for_each_safe(item, next, &surface->item_list,
+								surface_link) {
+			if (item->panel == panel)
+				panel_list_item_remove(item);
+		}
+	}
+
+	panel_window_list_schedule_redraw(panel);
+}
+
+static void
+desktop_destroy_surface(struct surface *surface)
+{
+	struct list_item *item, *next;
+
+	wl_list_for_each_safe(item, next, &surface->item_list, surface_link)
+		panel_list_item_remove(item);
+
+	wl_list_remove(&surface->link);
+	free(surface->title);
+	free(surface);
+}
+
+static void
+desktop_update_list_items(struct desktop *desktop, struct surface *surface)
+{
+	struct output *output;
+
+	wl_list_for_each(output, &desktop->outputs, link)
+		output_update_window_list(output, surface);
+}
+
+static void
+surface_data_set_output_mask(void *data,
+				struct surface_data *surface_data,
+				uint32_t output_mask)
+{
+	struct desktop *desktop;
+	struct surface *surface = data;
+
+	desktop = surface->desktop;
+
+	surface->output_mask = output_mask;
+
+	desktop_update_list_items(desktop, surface);
+}
+
+static void
+surface_data_set_title(void *data,
+				struct surface_data *surface_data,
+				const char *title)
+{
+	struct desktop *desktop;
+	struct surface *surface = data;
+
+	desktop = surface->desktop;
+
+	if (surface->title)
+		free(surface->title);
+	surface->title = strdup(title);
+
+	desktop_update_list_items(desktop, surface);
+}
+
+static void
+surface_data_destroy_handler(void *data, struct surface_data *surface_data)
+{
+	struct list_item *item, *next;
+	struct desktop *desktop;
+	struct surface *surface = data;
+	struct output *output;
+	struct panel *panel;
+
+	desktop = surface->desktop;
+
+	surface_data_destroy(surface_data);
+
+	wl_list_for_each(output, &desktop->outputs, link) {
+		panel = output->panel;
+		wl_list_for_each_safe(item, next, &panel->window_list, link) {
+			if (surface_data == item->surface->surface_data) {
+				desktop_destroy_surface(item->surface);
+				return;
+			}
+		}
+	}
+}
+
+static const struct surface_data_listener surface_data_listener = {
+	surface_data_set_output_mask,
+	surface_data_set_title,
+	surface_data_destroy_handler
+};
+
+static void
+surface_data_receive_surface_object(void *data,
+				struct surface_data_manager *manager,
+				struct surface_data *surface_data)
+{
+	struct desktop *desktop = data;
+	struct surface *surface;
+
+	surface = calloc(1, sizeof *surface);
+
+	if (!surface) {
+		fprintf(stderr, "ERROR: Failed to allocate memory!\n");
+		exit(EXIT_FAILURE);
+	}
+
+	surface->surface_data = surface_data;
+	surface->desktop = desktop;
+	surface->title = strdup("unknown");
+	surface->output_mask = 1;
+	wl_list_init(&surface->item_list);
+	wl_list_insert(&desktop->surfaces, &surface->link);
+	surface_data_add_listener(surface_data,
+				   &surface_data_listener, surface);
+}
+
+static const struct surface_data_manager_listener surface_data_manager_listener = {
+	surface_data_receive_surface_object
+};
+
 static struct background *
 background_create(struct desktop *desktop)
 {
@@ -1022,6 +1452,15 @@ desktop_destroy_outputs(struct desktop *desktop)
 }
 
 static void
+desktop_destroy_surfaces(struct desktop *desktop)
+{
+	struct surface *surface, *next;
+
+	wl_list_for_each_safe(surface, next, &desktop->surfaces, link)
+		desktop_destroy_surface(surface);
+}
+
+static void
 create_output(struct desktop *desktop, uint32_t id)
 {
 	struct output *output;
@@ -1033,6 +1472,8 @@ create_output(struct desktop *desktop, uint32_t id)
 	output->output =
 		display_bind(desktop->display, id, &wl_output_interface, 1);
 
+	output->id = desktop->output_count++;
+
 	wl_list_insert(&desktop->outputs, &output->link);
 }
 
@@ -1048,6 +1489,12 @@ global_handler(struct display *display, uint32_t id,
 		desktop_shell_add_listener(desktop->shell, &listener, desktop);
 	} else if (!strcmp(interface, "wl_output")) {
 		create_output(desktop, id);
+	} else if (!strcmp(interface, "surface_data_manager")) {
+		desktop->surface_data_manager =
+				display_bind(display, id,
+					&surface_data_manager_interface, 1);
+		surface_data_manager_add_listener(desktop->surface_data_manager,
+					&surface_data_manager_listener, desktop);
 	}
 }
 
@@ -1100,6 +1547,9 @@ int main(int argc, char *argv[])
 		return -1;
 	}
 
+	wl_list_init(&desktop.surfaces);
+	desktop.output_count = 0;
+
 	display_set_user_data(desktop.display, &desktop);
 	display_set_global_handler(desktop.display, global_handler);
 
@@ -1133,6 +1583,7 @@ int main(int argc, char *argv[])
 
 	/* Cleanup */
 	grab_surface_destroy(&desktop);
+	desktop_destroy_surfaces(&desktop);
 	desktop_destroy_outputs(&desktop);
 	if (desktop.unlock_dialog)
 		unlock_dialog_destroy(desktop.unlock_dialog);
diff --git a/data/Makefile.am b/data/Makefile.am
index a7cc944..293d2bc 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -7,6 +7,7 @@ dist_westondata_DATA =				\
 	terminal.png				\
 	border.png				\
 	icon_window.png				\
+	list_item_icon.png			\
 	sign_close.png				\
 	sign_maximize.png			\
 	sign_minimize.png
diff --git a/data/list_item_icon.png b/data/list_item_icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac987e997313910ec3e02a11be8ccda0dfa581c7
GIT binary patch
literal 176
zcmeAS at N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b
z3=G`DAk4 at xYmNj^kiEpy*OmPaH$RJ!-UgFp7lA^yo-U3d7N_4%cI0AE;5i(0^Z))B
z|EBQpgWZ)55eu3H82wmS6TZt#lE`$@S at Cy{aM-usE&4llHh-IQIlXHpO8~cw3TFo|
P&>#j+S3j3^P6<r_g#|R0

literal 0
HcmV?d00001

diff --git a/src/compositor.c b/src/compositor.c
index a2860fd..c6637c3 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -509,6 +509,8 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
 		if (1 << output->id & left)
 			wl_surface_send_leave(&es->surface.resource, resource);
 	}
+
+	wl_signal_emit(&es->compositor->output_mask_update_signal, es);
 }
 
 static void
@@ -3049,6 +3051,7 @@ weston_compositor_init(struct weston_compositor *ec,
 	wl_signal_init(&ec->wake_signal);
 	wl_signal_init(&ec->show_input_panel_signal);
 	wl_signal_init(&ec->hide_input_panel_signal);
+	wl_signal_init(&ec->output_mask_update_signal);
 	wl_signal_init(&ec->seat_created_signal);
 	ec->launcher_sock = weston_environment_get_fd("WESTON_LAUNCHER_SOCK");
 
diff --git a/src/compositor.h b/src/compositor.h
index 4a0c1e3..36ec337 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -301,6 +301,7 @@ struct weston_compositor {
 
 	struct wl_signal show_input_panel_signal;
 	struct wl_signal hide_input_panel_signal;
+	struct wl_signal output_mask_update_signal;
 
 	struct wl_signal seat_created_signal;
 
diff --git a/src/shell.c b/src/shell.c
index 3da5321..5f09a45 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -88,6 +88,7 @@ struct desktop_shell {
 	struct wl_listener destroy_listener;
 	struct wl_listener show_input_panel_listener;
 	struct wl_listener hide_input_panel_listener;
+	struct wl_listener output_mask_update_listener;
 
 	struct weston_layer fullscreen_layer;
 	struct weston_layer panel_layer;
@@ -107,6 +108,8 @@ struct desktop_shell {
 		uint32_t deathstamp;
 	} child;
 
+	struct wl_resource *surface_data_manager;
+
 	bool locked;
 	bool showing_input_panels;
 	bool prepare_event_sent;
@@ -217,6 +220,7 @@ struct shell_surface {
 	struct wl_list link;
 
 	const struct weston_shell_client *client;
+	struct wl_resource *surface_data;
 };
 
 struct shell_grab {
@@ -1415,6 +1419,110 @@ shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
 }
 
 static void
+surface_data_object_destroy(struct wl_resource *resource)
+{
+	struct shell_surface *shsurf = resource->data;
+
+	free(resource);
+
+	if (!shsurf)
+		return;
+
+	shsurf->surface_data = NULL;
+}
+
+static void
+surface_data_destroy_handler(struct wl_client *client,
+					struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct surface_data_interface
+					surface_data_implementation = {
+	surface_data_destroy_handler
+};
+
+static int
+create_surface_data(struct desktop_shell *shell, struct shell_surface *shsurf)
+{
+	struct wl_resource *surface_data;
+
+	if (shsurf->surface_data)
+		return -1;
+
+	surface_data = malloc(sizeof *surface_data);
+	if (surface_data == NULL)
+		return -1;
+
+	surface_data->data = shsurf;
+	surface_data->object.id = 0;
+	surface_data->object.interface = &surface_data_interface;
+	surface_data->destroy = surface_data_object_destroy;
+	surface_data->object.implementation =
+			(void (**)(void)) &surface_data_implementation;
+	wl_signal_init(&surface_data->destroy_signal);
+
+	wl_client_add_resource(shell->surface_data_manager->client, surface_data);
+
+	shsurf->surface_data = surface_data;
+
+	surface_data_manager_send_surface_object(shell->surface_data_manager,
+					shsurf->surface_data);
+
+	return 0;
+}
+
+static bool
+surface_is_window_list_candidate(struct weston_surface *surface)
+{
+	struct desktop_shell *shell;
+	struct shell_surface *shsurf;
+
+	shsurf = get_shell_surface(surface);
+	if (!shsurf)
+		return false;
+
+	shell = shsurf->shell;
+
+	if (!shell->surface_data_manager)
+		return false;
+
+	switch (shsurf->type) {
+	default:
+	case SHELL_SURFACE_TRANSIENT:
+	case SHELL_SURFACE_POPUP:
+	case SHELL_SURFACE_NONE:
+		return false;
+	case SHELL_SURFACE_FULLSCREEN:
+	case SHELL_SURFACE_MAXIMIZED:
+	case SHELL_SURFACE_TOPLEVEL:
+		return true;
+	}
+}
+
+static void
+send_surface_data_output_mask(struct weston_surface *surface)
+{
+	struct shell_surface *shsurf = get_shell_surface(surface);
+
+	if (shsurf && shsurf->surface_data)
+		surface_data_send_output_mask(shsurf->surface_data,
+						surface->output_mask);
+}
+
+static void
+send_surface_data_title(struct weston_surface *surface)
+{
+	struct shell_surface *shsurf = get_shell_surface(surface);
+
+	if (shsurf && shsurf->surface_data)
+		surface_data_send_title(shsurf->surface_data,
+						shsurf->title == NULL ?
+						"Surface" : shsurf->title);
+}
+
+static void
 shell_surface_set_title(struct wl_client *client,
 			struct wl_resource *resource, const char *title)
 {
@@ -1422,6 +1530,7 @@ shell_surface_set_title(struct wl_client *client,
 
 	free(shsurf->title);
 	shsurf->title = strdup(title);
+	send_surface_data_title(shsurf->surface);
 }
 
 static void
@@ -1538,6 +1647,10 @@ set_surface_type(struct shell_surface *shsurf)
 	default:
 		break;
 	}
+
+	if (surface_is_window_list_candidate(shsurf->surface))
+		create_surface_data(shsurf->shell, shsurf);
+	send_surface_data_title(surface);
 }
 
 static void
@@ -1984,6 +2097,8 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {
 static void
 destroy_shell_surface(struct shell_surface *shsurf)
 {
+	if (shsurf->surface_data)
+		surface_data_send_gone(shsurf->surface_data);
 	if (shsurf->popup.grab.pointer)
 		wl_pointer_end_grab(shsurf->popup.grab.pointer);
 
@@ -2390,6 +2505,37 @@ static const struct desktop_shell_interface desktop_shell_implementation = {
 	desktop_shell_set_grab_surface
 };
 
+static void
+surface_data_create_all_objects(struct desktop_shell *shell)
+{
+	struct weston_surface *surface;
+	struct shell_surface *shsurf;
+	struct workspace *ws;
+
+	ws = get_current_workspace(shell);
+
+	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+		if (surface_is_window_list_candidate(surface)) {
+			shsurf = get_shell_surface(surface);
+			create_surface_data(shell, shsurf);
+		}
+	}
+}
+
+static void
+surface_data_send_all_info(struct desktop_shell *shell)
+{
+	struct weston_surface *surface;
+	struct workspace *ws;
+
+	ws = get_current_workspace(shell);
+
+	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+		send_surface_data_output_mask(surface);
+		send_surface_data_title(surface);
+	}
+}
+
 static enum shell_surface_type
 get_shell_surface_type(struct weston_surface *surface)
 {
@@ -2946,6 +3092,22 @@ show_input_panels(struct wl_listener *listener, void *data)
 }
 
 static void
+output_mask_update(struct wl_listener *listener, void *data)
+{
+	struct weston_surface *surface = data;
+	struct shell_surface *shsurf;
+
+	if (!surface)
+		return;
+
+	shsurf = get_shell_surface(surface);
+
+	if (shsurf && shsurf->surface_data)
+		surface_data_send_output_mask(shsurf->surface_data,
+						surface->output_mask);
+}
+
+static void
 hide_input_panels(struct wl_listener *listener, void *data)
 {
 	struct desktop_shell *shell =
@@ -3304,6 +3466,38 @@ bind_desktop_shell(struct wl_client *client,
 }
 
 static void
+unbind_surface_data_manager(struct wl_resource *resource)
+{
+	struct desktop_shell *shell = resource->data;
+
+	shell->surface_data_manager = NULL;
+	free(resource);
+}
+
+static void
+bind_surface_data_manager(struct wl_client *client,
+		   void *data, uint32_t version, uint32_t id)
+{
+	struct desktop_shell *shell = data;
+	struct wl_resource *resource;
+
+	resource = wl_client_add_object(client, &surface_data_manager_interface,
+					NULL, id, shell);
+
+	if (client == shell->child.client) {
+		resource->destroy = unbind_surface_data_manager;
+		shell->surface_data_manager = resource;
+		surface_data_create_all_objects(shell);
+		surface_data_send_all_info(shell);
+		return;
+	}
+
+	wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+			       "permission to bind desktop_shell denied");
+	wl_resource_destroy(resource);
+}
+
+static void
 screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy, int32_t width, int32_t height)
 {
 	struct desktop_shell *shell = surface->private;
@@ -3978,6 +4172,7 @@ shell_destroy(struct wl_listener *listener, void *data)
 	wl_list_remove(&shell->wake_listener.link);
 	wl_list_remove(&shell->show_input_panel_listener.link);
 	wl_list_remove(&shell->hide_input_panel_listener.link);
+	wl_list_remove(&shell->output_mask_update_listener.link);
 
 	wl_array_for_each(ws, &shell->workspaces.array)
 		workspace_destroy(*ws);
@@ -4087,6 +4282,8 @@ module_init(struct weston_compositor *ec,
 	wl_signal_add(&ec->show_input_panel_signal, &shell->show_input_panel_listener);
 	shell->hide_input_panel_listener.notify = hide_input_panels;
 	wl_signal_add(&ec->hide_input_panel_signal, &shell->hide_input_panel_listener);
+	shell->output_mask_update_listener.notify = output_mask_update;
+	wl_signal_add(&ec->output_mask_update_signal, &shell->output_mask_update_listener);
 	ec->ping_handler = ping_handler;
 	ec->shell_interface.shell = shell;
 	ec->shell_interface.create_shell_surface = create_shell_surface;
@@ -4144,6 +4341,10 @@ module_init(struct weston_compositor *ec,
 	if (wl_display_add_global(ec->wl_display, &workspace_manager_interface,
 				  shell, bind_workspace_manager) == NULL)
 		return -1;
+	if (wl_display_add_global(ec->wl_display, &surface_data_manager_interface,
+				  shell, bind_surface_data_manager) == NULL)
+		return -1;
+
 
 	shell->child.deathstamp = weston_compositor_get_time();
 
-- 
1.7.10.4



More information about the wayland-devel mailing list