Hi Kristian,<br><br>Sorry it took so long to get back to you on this but
I had to deal with some other more pressing things. I wanted to reply while I am rebasing
this series to master, in case there are any other concerns. Please see responses below.<br><br><div class="gmail_quote">On Wed, Nov 28, 2012 at 7:48 PM, Kristian Høgsberg <span dir="ltr"><<a href="mailto:hoegsberg@gmail.com" target="_blank">hoegsberg@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>On Thu, Nov 15, 2012 at 12:37:01AM -0700, Scott Moreau wrote:<br>
> This patch uses the special surface_data interface to send information about<br>
> the surface to the shell. The shell then uses this information to render a<br>
> window list in the panel.<br>
<br>
</div>The previous XML patch was good and the overall approach here is fine.<br>
There's just a few more things below we need to fix and then we can<br>
commit this.<br></blockquote><div><br>Cool.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> ---<br>
> clients/desktop-shell.c | 526 ++++++++++++++++++++++++++++++++++++++++++++++--<br>
> data/Makefile.am | 1 +<br>
> data/list_item_icon.png | Bin 0 -> 176 bytes<br>
> src/compositor.c | 2 +<br>
> src/compositor.h | 1 +<br>
> src/shell.c | 170 ++++++++++++++++<br>
> 6 files changed, 683 insertions(+), 17 deletions(-)<br>
> create mode 100644 data/list_item_icon.png<br>
><br>
> diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c<br>
> index 1cae789..ea42c66 100644<br>
> --- a/clients/desktop-shell.c<br>
> +++ b/clients/desktop-shell.c<br>
> @@ -44,6 +44,8 @@<br>
><br>
> #include "desktop-shell-client-protocol.h"<br>
><br>
> +#define MIN(x, y) (((x) < (y)) ? (x) : (y))<br>
> +<br>
> extern char **environ; /* defined by libc */<br>
><br>
> struct desktop {<br>
> @@ -51,37 +53,59 @@ struct desktop {<br>
> struct desktop_shell *shell;<br>
> struct unlock_dialog *unlock_dialog;<br>
> struct task unlock_task;<br>
> + struct wl_list surfaces;<br>
> struct wl_list outputs;<br>
> + uint32_t output_count;<br>
><br>
> struct window *grab_window;<br>
> struct widget *grab_widget;<br>
><br>
> enum cursor_type grab_cursor;<br>
> +<br>
> + struct surface_data_manager *surface_data_manager;<br>
> };<br>
><br>
> struct surface {<br>
> + struct wl_list item_list;<br>
> + struct surface_data *surface_data;<br>
> + uint32_t output_mask;<br>
> + char *title;<br>
> +<br>
> + struct wl_list link;<br>
> +};<br>
> +<br>
> +struct resize {<br>
> void (*configure)(void *data,<br>
> struct desktop_shell *desktop_shell,<br>
> uint32_t edges, struct window *window,<br>
> int32_t width, int32_t height);<br>
> };<br>
><br>
> +struct rgba {<br>
> + float r, g, b, a;<br>
> +};<br>
> +<br>
> struct panel {<br>
> - struct surface base;<br>
> + struct resize base;<br>
> struct window *window;<br>
> struct widget *widget;<br>
> struct wl_list launcher_list;<br>
> + struct wl_list window_list;<br>
> + struct rectangle window_list_rect;<br>
> + uint32_t surface_count;<br>
> + struct rgba focused_item;<br>
> struct panel_clock *clock;<br>
> };<br>
><br>
> struct background {<br>
> - struct surface base;<br>
> + struct resize base;<br>
> struct window *window;<br>
> struct widget *widget;<br>
> };<br>
><br>
> struct output {<br>
> struct wl_output *output;<br>
> + uint32_t id;<br>
> struct wl_list link;<br>
><br>
> struct panel *panel;<br>
> @@ -99,6 +123,16 @@ struct panel_launcher {<br>
> struct wl_array argv;<br>
> };<br>
><br>
> +struct list_item {<br>
> + struct surface *surface;<br>
> + struct widget *widget;<br>
> + struct panel *panel;<br>
> + cairo_surface_t *icon;<br>
> + int focused, highlight;<br>
> + struct wl_list link;<br>
> + struct wl_list surface_link;<br>
> +};<br>
> +<br>
> struct panel_clock {<br>
> struct widget *widget;<br>
> struct panel *panel;<br>
> @@ -249,6 +283,15 @@ set_hex_color(cairo_t *cr, uint32_t color)<br>
> }<br>
><br>
> static void<br>
> +get_hex_color_rgba(uint32_t color, float *r, float *g, float *b, float *a)<br>
> +{<br>
> + *r = ((color >> 16) & 0xff) / 255.0;<br>
> + *g = ((color >> 8) & 0xff) / 255.0;<br>
> + *b = ((color >> 0) & 0xff) / 255.0;<br>
> + *a = ((color >> 24) & 0xff) / 255.0;<br>
> +}<br>
> +<br>
> +static void<br>
> panel_redraw_handler(struct widget *widget, void *data)<br>
> {<br>
> cairo_surface_t *surface;<br>
> @@ -421,15 +464,49 @@ panel_button_handler(struct widget *widget,<br>
> }<br>
><br>
> static void<br>
> +panel_window_list_schedule_redraw(struct panel *panel)<br>
> +{<br>
> + struct list_item *item;<br>
> + float x, y, w, h;<br>
> + float item_width, padding;<br>
> +<br>
> + /* If there are no window list items, redraw the panel to clear it */<br>
> + if (wl_list_empty(&panel->window_list)) {<br>
> + widget_schedule_redraw(panel->widget);<br>
> + return;<br>
> + }<br>
> +<br>
> + item_width = ((float) panel->window_list_rect.width /<br>
> + panel->surface_count);<br>
> + padding = MIN(item_width * 0.1f, 2.0f);<br>
<br>
</div></div>We don't want floats here. We're trying to compute an integer number<br>
of pixels that will be the item width. Also, item padding is always a<br>
constant number of pixels (2 is fine), not proportional to the width.<br></blockquote><div><br>Yes, you are right.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><br>
> + x = panel->window_list_rect.x + padding;<br>
> + y = 16;<br>
> + w = MIN(item_width - padding, 200);<br>
> + h = 24;<br>
> +<br>
> + wl_list_for_each(item, &panel->window_list, link) {<br>
> + widget_set_allocation(item->widget, x, y - h / 2, w + 1, h + 1);<br>
<br>
</div>It'd be more straightforward to just use x, 4, w, 24 here. I know<br>
it's done that way elsewhere in desktop-shell.c but it doesn't make<br>
sense there either.<br></blockquote><div><br>Done.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + x += w + padding;<br>
> + widget_schedule_redraw(item->widget);<br>
> + }<br>
> +}<br>
> +<br>
> +static void<br>
> panel_resize_handler(struct widget *widget,<br>
> int32_t width, int32_t height, void *data)<br>
> {<br>
> struct panel_launcher *launcher;<br>
> + struct rectangle launcher_rect;<br>
> + struct rectangle clock_rect;<br>
> struct panel *panel = data;<br>
> int x, y, w, h;<br>
> -<br>
> +<br>
> x = 10;<br>
> y = 16;<br>
> +<br>
> + launcher_rect.x = x;<br>
> +<br>
> wl_list_for_each(launcher, &panel->launcher_list, link) {<br>
> w = cairo_image_surface_get_width(launcher->icon);<br>
> h = cairo_image_surface_get_height(launcher->icon);<br>
> @@ -437,12 +514,25 @@ panel_resize_handler(struct widget *widget,<br>
> x, y - h / 2, w + 1, h + 1);<br>
> x += w + 10;<br>
<br>
</div></div>We could fix the weird y - h / 2 here too.<br></blockquote><div><br>No problem.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> }<br>
> - h=20;<br>
> +<br>
> + launcher_rect.width = x - launcher_rect.x;<br>
> +<br>
> w=170;<br>
> + h=20;<br>
><br>
> if (panel->clock)<br>
> widget_set_allocation(panel->clock->widget,<br>
> width - w - 8, y - h / 2, w + 1, h + 1);<br>
> +<br>
> + widget_get_allocation(panel->clock->widget, &clock_rect);<br>
> +<br>
> + panel->window_list_rect.x = launcher_rect.x + launcher_rect.width;<br>
> + panel->window_list_rect.y = 2;<br>
> + panel->window_list_rect.width = width -<br>
> + panel->window_list_rect.x -<br>
> + (clock_rect.width + 20);<br>
> + panel->window_list_rect.height = 28;<br>
> + panel_window_list_schedule_redraw(panel);<br>
> }<br>
><br>
> static void<br>
> @@ -451,7 +541,7 @@ panel_configure(void *data,<br>
> uint32_t edges, struct window *window,<br>
> int32_t width, int32_t height)<br>
> {<br>
> - struct surface *surface = window_get_user_data(window);<br>
> + struct resize *surface = window_get_user_data(window);<br>
> struct panel *panel = container_of(surface, struct panel, base);<br>
><br>
> window_schedule_resize(panel->window, width, 32);<br>
> @@ -490,6 +580,25 @@ panel_destroy(struct panel *panel)<br>
> free(panel);<br>
> }<br>
><br>
> +static void<br>
> +panel_set_list_item_focus_color(struct panel *panel)<br>
> +{<br>
> + float r, g, b, a;<br>
> +<br>
> + /* Consider panel color when choosing item highlight color */<br>
> + get_hex_color_rgba(key_panel_color, &r, &b, &g, &a);<br>
> + if (r += 0.2, g += 0.2, b += 0.2, r > 1.0 || g > 1.0 || b > 1.0) {<br>
<br>
</div></div>Can we avoid all this stuff inside the if? Also, just saturating to<br>
white might be better.<br></blockquote><div><br>Yes we can avoid the statements in the conditional but I don't think fading to white is a good idea. This code attempts to detect lighter color panels near white and sets the highlight color of the list items to a darker color to offset. I did restructure it a bit however. This is what I have currently:<br>
<br><br> /* Consider panel color when choosing item highlight color */<br> get_hex_color_rgba(key_panel_color, &r, &b, &g, &a);<br> r += 0.2;<br> g += 0.2;<br> b += 0.2;<br> panel->focused_item.r = r > 1.0 ? 0.6 : r;<br>
panel->focused_item.g = g > 1.0 ? 0.6 : g;<br> panel->focused_item.b = b > 1.0 ? 0.6 : b;<br> panel->focused_item.a = 0.75;<br> <br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + panel->focused_item.r = 0.6;<br>
> + panel->focused_item.g = 0.6;<br>
> + panel->focused_item.b = 0.6;<br>
> + } else {<br>
> + panel->focused_item.r = r;<br>
> + panel->focused_item.g = g;<br>
> + panel->focused_item.b = b;<br>
> + }<br>
> + panel->focused_item.a = 0.75;<br>
> +}<br>
> +<br>
> static struct panel *<br>
> panel_create(struct display *display)<br>
> {<br>
> @@ -502,6 +611,7 @@ panel_create(struct display *display)<br>
> panel->window = window_create_custom(display);<br>
> panel->widget = window_add_widget(panel->window, panel);<br>
> wl_list_init(&panel->launcher_list);<br>
> + wl_list_init(&panel->window_list);<br>
><br>
> window_set_title(panel->window, "panel");<br>
> window_set_user_data(panel->window, panel);<br>
> @@ -509,7 +619,9 @@ panel_create(struct display *display)<br>
> widget_set_redraw_handler(panel->widget, panel_redraw_handler);<br>
> widget_set_resize_handler(panel->widget, panel_resize_handler);<br>
> widget_set_button_handler(panel->widget, panel_button_handler);<br>
> -<br>
> +<br>
> + panel->surface_count = 0;<br>
> + panel_set_list_item_focus_color(panel);<br>
> panel_add_clock(panel);<br>
><br>
> return panel;<br>
> @@ -518,18 +630,21 @@ panel_create(struct display *display)<br>
> static cairo_surface_t *<br>
> load_icon_or_fallback(const char *icon)<br>
> {<br>
> - cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);<br>
> + cairo_surface_t *surface;<br>
> cairo_t *cr;<br>
> -<br>
> - if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS)<br>
> - return surface;<br>
> -<br>
> - cairo_surface_destroy(surface);<br>
> - fprintf(stderr, "ERROR loading icon from file '%s'\n", icon);<br>
> +#ifdef CAIRO_HAS_PNG_FUNCTIONS<br>
> + if (icon) {<br>
> + surface = cairo_image_surface_create_from_png(icon);<br>
<br>
</div></div>We should just use load_cairo_surface() here instead of using the<br>
cairo png functions. That lets us load other formats and we don't<br>
need the #ifdef.<br></blockquote><div><br>Yes, this is much better since it adds support for other image types as well.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + if (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS)<br>
> + return surface;<br>
> +<br>
> + cairo_surface_destroy(surface);<br>
> + fprintf(stderr, "ERROR loading icon from file '%s'\n", icon);<br>
> + }<br>
> +#endif<br>
><br>
> /* draw fallback icon */<br>
> - surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,<br>
> - 20, 20);<br>
> + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 20, 20);<br>
> cr = cairo_create(surface);<br>
><br>
> cairo_set_source_rgba(cr, 0.8, 0.8, 0.8, 1);<br>
> @@ -862,7 +977,7 @@ desktop_shell_configure(void *data,<br>
> int32_t width, int32_t height)<br>
> {<br>
> struct window *window = wl_surface_get_user_data(surface);<br>
> - struct surface *s = window_get_user_data(window);<br>
> + struct resize *s = window_get_user_data(window);<br>
><br>
> s->configure(data, desktop_shell, edges, window, width, height);<br>
> }<br>
> @@ -946,6 +1061,362 @@ background_destroy(struct background *background)<br>
> free(background);<br>
> }<br>
><br>
> +static void<br>
> +panel_list_item_redraw_handler(struct widget *widget, void *data)<br>
> +{<br>
> + cairo_t *cr;<br>
> + cairo_surface_t *surface;<br>
> + struct list_item *item = data;<br>
> + struct rectangle rect;<br>
> + cairo_text_extents_t extents;<br>
> + cairo_font_extents_t font_extents;<br>
> + int icon_width, icon_height;<br>
> + unsigned int dots;<br>
> + double padding;<br>
> + char title[128];<br>
> +<br>
> + widget_get_allocation(widget, &rect);<br>
> + if (rect.width == 0)<br>
> + return;<br>
> +<br>
> + surface = window_get_surface(item->panel->window);<br>
> + cr = cairo_create(surface);<br>
> +<br>
> + if (item->focused) {<br>
> + cairo_set_source_rgba(cr, item->panel->focused_item.r,<br>
> + item->panel->focused_item.g,<br>
> + item->panel->focused_item.b,<br>
> + item->panel->focused_item.a);<br>
> + cairo_move_to(cr, rect.x, rect.y);<br>
> + cairo_line_to(cr, rect.x + rect.width, rect.y);<br>
> + cairo_line_to(cr, rect.x + rect.width, rect.y + rect.height);<br>
> + cairo_line_to(cr, rect.x, rect.y + rect.height);<br>
> + cairo_line_to(cr, rect.x, rect.y);<br>
<br>
</div></div>Use cairo_rectangle() instead?<br></blockquote><div><br>Yep.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + cairo_fill(cr);<br>
> + }<br>
> +<br>
> + icon_width = cairo_image_surface_get_width(item->icon);<br>
> + icon_height = cairo_image_surface_get_height(item->icon);<br>
> + padding = (rect.height / 2.f) - (icon_height / 2.f);<br>
> + if (rect.width > icon_width * 2) {<br>
> + cairo_set_source_surface(cr, item->icon,<br>
> + rect.x + padding,<br>
> + rect.y + padding);<br>
> + cairo_paint(cr);<br>
> + } else {<br>
> + icon_width = 0;<br>
> + icon_height = 0;<br>
> + padding = 1;<br>
> + }<br>
> +<br>
> + strcpy(title, item->surface->title);<br>
> + cairo_select_font_face(cr, "sans",<br>
> + CAIRO_FONT_SLANT_NORMAL,<br>
> + CAIRO_FONT_WEIGHT_NORMAL);<br>
> + cairo_set_font_size(cr, 14);<br>
> + cairo_text_extents(cr, title, &extents);<br>
> +<br>
> + /* If the string is too long, clip text to button width */<br>
> + while (extents.width > (rect.width - (icon_width + padding * 3))) {<br>
> + title[strlen(title) - 1] = '\0';<br>
> + cairo_text_extents(cr, title, &extents);<br>
> + if (extents.width <= 0) {<br>
> + title[0] = '\0';<br>
> + break;<br>
> + }<br>
> + }<br>
> +<br>
> + /* If the text is clipped, add an ellipsis */<br>
> + dots = 3;<br>
> + if (strlen(title) < dots)<br>
> + dots = strlen(title) + 1;<br>
> + if (strlen(title) != strlen(item->surface->title))<br>
> + while (dots-- > 0)<br>
> + title[strlen(title) - dots] = '.';<br>
<br>
</div></div>I suppose this is fine... if we want to do better, we'll have to use<br>
something like pango or harfbuzz to ellipsize the text. For our toy<br>
desktop, this is par for the course.<br>
<div><div><br>
> + cairo_font_extents (cr, &font_extents);<br>
> + cairo_move_to(cr, rect.x + icon_width + padding * 3 + 1,<br>
> + rect.y + 3 * (rect.height >> 2) + 1);<br>
> + cairo_set_source_rgb(cr, 0, 0, 0);<br>
> + cairo_show_text(cr, title);<br>
> + cairo_move_to(cr, rect.x + icon_width + padding * 3,<br>
> + rect.y + 3 * (rect.height >> 2));<br>
> + if (item->focused)<br>
> + cairo_set_source_rgb(cr, 1, 1, 1);<br>
> + else<br>
> + cairo_set_source_rgb(cr, 0.85, 0.85, 0.85);<br>
> + cairo_show_text(cr, title);<br>
> + cairo_destroy(cr);<br>
> +}<br>
> +<br>
> +static int<br>
> +panel_list_item_motion_handler(struct widget *widget, struct input *input,<br>
> + uint32_t time, float x, float y, void *data)<br>
> +{<br>
> + struct list_item *item = data;<br>
> +<br>
> + widget_set_tooltip(widget, basename((char *)item->surface->title), x, y);<br>
> +<br>
> + return CURSOR_LEFT_PTR;<br>
> +}<br>
> +<br>
> +static int<br>
> +panel_list_item_enter_handler(struct widget *widget, struct input *input,<br>
> + float x, float y, void *data)<br>
> +{<br>
> + struct list_item *item = data;<br>
> +<br>
> + item->focused = 1;<br>
> + widget_schedule_redraw(widget);<br>
> +<br>
> + return CURSOR_LEFT_PTR;<br>
> +}<br>
> +<br>
> +static void<br>
> +panel_list_item_leave_handler(struct widget *widget,<br>
> + struct input *input, void *data)<br>
> +{<br>
> + struct list_item *item = data;<br>
> +<br>
> + item->focused = 0;<br>
> + widget_destroy_tooltip(widget);<br>
> + widget_schedule_redraw(widget);<br>
> +}<br>
> +<br>
> +static void<br>
> +panel_list_item_button_handler(struct widget *widget,<br>
> + struct input *input, uint32_t time,<br>
> + uint32_t button,<br>
> + enum wl_pointer_button_state state, void *data)<br>
> +{<br>
> + widget_schedule_redraw(widget);<br>
> + /* TODO: Toggle minimize */<br>
> +}<br>
> +<br>
> +static struct list_item *<br>
> +panel_list_item_add(struct panel *panel, const char *icon, const char *text)<br>
> +{<br>
> + struct list_item *item;<br>
> + item = malloc(sizeof *item);<br>
> + memset(item, 0, sizeof *item);<br>
> +<br>
> + item->icon = load_icon_or_fallback(icon);<br>
> +<br>
> + item->panel = panel;<br>
> + wl_list_insert(panel->window_list.prev, &item->link);<br>
> + panel->surface_count++;<br>
> +<br>
> + item->widget = widget_add_widget(panel->widget, item);<br>
> + widget_set_enter_handler(item->widget, panel_list_item_enter_handler);<br>
> + widget_set_leave_handler(item->widget, panel_list_item_leave_handler);<br>
> + widget_set_button_handler(item->widget, panel_list_item_button_handler);<br>
> + widget_set_redraw_handler(item->widget, panel_list_item_redraw_handler);<br>
> + widget_set_motion_handler(item->widget, panel_list_item_motion_handler);<br>
> +<br>
> + return item;<br>
> +}<br>
> +<br>
> +static void<br>
> +panel_list_item_remove(struct list_item *item)<br>
> +{<br>
> + item->panel->surface_count--;<br>
> + wl_list_remove(&item->link);<br>
> + wl_list_remove(&item->surface_link);<br>
> + widget_destroy(item->widget);<br>
> + panel_window_list_schedule_redraw(item->panel);<br>
> + free(item);<br>
> +}<br>
> +<br>
> +static int<br>
> +panel_list_item_exists(struct panel *panel, struct surface *surface)<br>
> +{<br>
> + struct list_item *p_item, *s_item;<br>
> +<br>
> + wl_list_for_each(p_item, &panel->window_list, link) {<br>
> + wl_list_for_each(s_item, &surface->item_list, surface_link) {<br>
> + if (p_item == s_item)<br>
> + return 1;<br>
> + }<br>
> + }<br>
> +<br>
> + return 0;<br>
> +}<br>
> +<br>
> +static void<br>
> +output_update_window_list(struct output *output, struct surface *surface)<br>
> +{<br>
> + struct list_item *item, *next;<br>
> + struct panel *panel;<br>
> +<br>
> + panel = output->panel;<br>
> +<br>
> + /* Make a list item for each panel of the surfaces output mask */<br>
> + if ((1 << output->id) & surface->output_mask) {<br>
> + if (!panel_list_item_exists(panel, surface)) {<br>
> + item = panel_list_item_add(panel,<br>
> + DATADIR "/weston/list_item_icon.png",<br>
> + surface->title);<br>
> + wl_list_insert(surface->item_list.prev,<br>
> + &item->surface_link);<br>
> + item->surface = surface;<br>
> + }<br>
> + } else {<br>
> + /* Remove item from panel if surface<br>
> + * is no longer on the output */<br>
> + wl_list_for_each_safe(item, next, &surface->item_list,<br>
> + surface_link) {<br>
> + if (item->panel == panel)<br>
> + panel_list_item_remove(item);<br>
> + }<br>
> + }<br>
> +<br>
> + panel_window_list_schedule_redraw(panel);<br>
> +}<br>
> +<br>
> +static struct surface*<br>
> +desktop_create_surface(struct desktop *desktop,<br>
> + struct surface_data *surface_data)<br>
> +{<br>
> + struct surface *surface;<br>
> +<br>
> + surface = calloc(1, sizeof *surface);<br>
> +<br>
> + if (!surface) {<br>
> + fprintf(stderr, "ERROR: Failed to allocate memory!\n");<br>
> + exit(EXIT_FAILURE);<br>
> + }<br>
> +<br>
> + surface->surface_data = surface_data;<br>
> + surface->title = strdup("unknown");<br>
> + surface->output_mask = 1;<br>
> + wl_list_init(&surface->item_list);<br>
> + wl_list_insert(&desktop->surfaces, &surface->link);<br>
> +<br>
> + return surface;<br>
> +}<br>
> +<br>
> +static void<br>
> +desktop_destroy_surface(struct surface *surface)<br>
> +{<br>
> + struct list_item *item, *next;<br>
> +<br>
> + wl_list_for_each_safe(item, next, &surface->item_list, surface_link)<br>
> + panel_list_item_remove(item);<br>
> +<br>
> + wl_list_remove(&surface->link);<br>
> + free(surface->title);<br>
> + free(surface);<br>
> +}<br>
> +<br>
> +static struct surface *<br>
> +desktop_get_surface(struct desktop *desktop, struct surface_data *surface_data)<br>
> +{<br>
> + struct output *output;<br>
> + struct list_item *item;<br>
> +<br>
> + wl_list_for_each(output, &desktop->outputs, link) {<br>
> + wl_list_for_each(item, &output->panel->window_list, link) {<br>
> + if (surface_data == item->surface->surface_data)<br>
> + return item->surface;<br>
> + }<br>
> + }<br>
<br>
</div></div>Just store struct surface as user_data for struct surface_data using<br>
surface_data_set_user_data(). Then this function becomes just<br>
surface_data_get_user_data().<br></blockquote><div><br>This seems like it will work.<br> </div><div></div><div></div><div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + return NULL;<br>
> +}<br>
> +<br>
> +static void<br>
> +desktop_update_list_items(struct desktop *desktop, struct surface *surface)<br>
> +{<br>
> + struct output *output;<br>
> +<br>
> + wl_list_for_each(output, &desktop->outputs, link)<br>
> + output_update_window_list(output, surface);<br>
> +}<br>
> +<br>
> +static void<br>
> +surface_data_set_output_mask(void *data,<br>
> + struct surface_data *surface_data,<br>
> + uint32_t output_mask)<br>
> +{<br>
> + struct desktop *desktop;<br>
> + struct surface *surface;<br>
> +<br>
> + desktop = data;<br>
> +<br>
> + surface = desktop_get_surface(desktop, surface_data);<br>
> +<br>
> + if (!surface)<br>
> + surface = desktop_create_surface(desktop, surface_data);<br>
> +<br>
> + surface->output_mask = output_mask;<br>
> +<br>
> + desktop_update_list_items(desktop, surface);<br>
> +}<br>
> +<br>
> +static void<br>
> +surface_data_set_title(void *data,<br>
> + struct surface_data *surface_data,<br>
> + const char *title)<br>
> +{<br>
> + struct desktop *desktop;<br>
> + struct surface *surface;<br>
> +<br>
> + desktop = data;<br>
> +<br>
> + surface = desktop_get_surface(desktop, surface_data);<br>
> +<br>
> + if (!surface)<br>
> + surface = desktop_create_surface(desktop, surface_data);<br>
> +<br>
> + if (surface->title)<br>
> + free(surface->title);<br>
> + surface->title = strdup(title);<br>
> +<br>
> + desktop_update_list_items(desktop, surface);<br>
> +}<br>
> +<br>
> +static void<br>
> +surface_data_destroy_handler(void *data, struct surface_data *surface_data)<br>
> +{<br>
> + struct list_item *item, *next;<br>
> + struct desktop *desktop;<br>
> + struct output *output;<br>
> + struct panel *panel;<br>
> +<br>
> + desktop = data;<br>
> +<br>
> + surface_data_destroy(surface_data);<br>
> +<br>
> + wl_list_for_each(output, &desktop->outputs, link) {<br>
> + panel = output->panel;<br>
> + wl_list_for_each_safe(item, next, &panel->window_list, link) {<br>
> + if (surface_data == item->surface->surface_data) {<br>
> + desktop_destroy_surface(item->surface);<br>
> + return;<br>
> + }<br>
> + }<br>
> + }<br>
> +}<br>
> +<br>
> +static const struct surface_data_listener surface_data_listener = {<br>
> + surface_data_set_output_mask,<br>
> + surface_data_set_title,<br>
> + surface_data_destroy_handler<br>
> +};<br>
> +<br>
> +static void<br>
> +surface_data_receive_surface_object(void *data,<br>
> + struct surface_data_manager *manager,<br>
> + struct surface_data *surface_data)<br>
> +{<br>
> + surface_data_add_listener(surface_data,<br>
> + &surface_data_listener, data);<br>
<br>
</div></div>Just always create struct surface here (fold desktop_create_surface()<br>
into this function and set the surface_data user data. struct surface<br>
will need a pointer back the struct desktop.<br></blockquote><div><br>Yep, sounds good.<br> </div><div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> +}<br>
> +<br>
> +static const struct surface_data_manager_listener surface_data_manager_listener = {<br>
> + surface_data_receive_surface_object<br>
> +};<br>
> +<br>
> static struct background *<br>
> background_create(struct desktop *desktop)<br>
> {<br>
> @@ -1022,6 +1493,15 @@ desktop_destroy_outputs(struct desktop *desktop)<br>
> }<br>
><br>
> static void<br>
> +desktop_destroy_surfaces(struct desktop *desktop)<br>
> +{<br>
> + struct surface *surface, *next;<br>
> +<br>
> + wl_list_for_each_safe(surface, next, &desktop->surfaces, link)<br>
> + desktop_destroy_surface(surface);<br>
> +}<br>
> +<br>
> +static void<br>
> create_output(struct desktop *desktop, uint32_t id)<br>
> {<br>
> struct output *output;<br>
> @@ -1033,7 +1513,9 @@ create_output(struct desktop *desktop, uint32_t id)<br>
> output->output =<br>
> display_bind(desktop->display, id, &wl_output_interface, 1);<br>
><br>
> - wl_list_insert(&desktop->outputs, &output->link);<br>
> + output->id = desktop->output_count++;<br>
> +<br>
> + wl_list_insert(desktop->outputs.prev, &output->link);<br>
> }<br>
><br>
> static void<br>
> @@ -1046,6 +1528,12 @@ global_handler(struct display *display, uint32_t id,<br>
> desktop->shell = display_bind(desktop->display,<br>
> id, &desktop_shell_interface, 1);<br>
> desktop_shell_add_listener(desktop->shell, &listener, desktop);<br>
> + } else if (strcmp(interface, "surface_data_manager") == 0) {<br>
> + desktop->surface_data_manager =<br>
> + display_bind(display, id,<br>
> + &surface_data_manager_interface, 1);<br>
> + surface_data_manager_add_listener(desktop->surface_data_manager,<br>
> + &surface_data_manager_listener, desktop);<br>
> } else if (!strcmp(interface, "wl_output")) {<br>
> create_output(desktop, id);<br>
> }<br>
> @@ -1100,6 +1588,9 @@ int main(int argc, char *argv[])<br>
> return -1;<br>
> }<br>
><br>
> + wl_list_init(&desktop.surfaces);<br>
> + desktop.output_count = 0;<br>
> +<br>
> display_set_user_data(desktop.display, &desktop);<br>
> display_set_global_handler(desktop.display, global_handler);<br>
><br>
> @@ -1133,6 +1624,7 @@ int main(int argc, char *argv[])<br>
><br>
> /* Cleanup */<br>
> grab_surface_destroy(&desktop);<br>
> + desktop_destroy_surfaces(&desktop);<br>
> desktop_destroy_outputs(&desktop);<br>
> if (desktop.unlock_dialog)<br>
> unlock_dialog_destroy(desktop.unlock_dialog);<br>
> diff --git a/data/Makefile.am b/data/Makefile.am<br>
> index a7cc944..293d2bc 100644<br>
> --- a/data/Makefile.am<br>
> +++ b/data/Makefile.am<br>
> @@ -7,6 +7,7 @@ dist_westondata_DATA = \<br>
> terminal.png \<br>
> border.png \<br>
> icon_window.png \<br>
> + list_item_icon.png \<br>
> sign_close.png \<br>
> sign_maximize.png \<br>
> sign_minimize.png<br>
> diff --git a/data/list_item_icon.png b/data/list_item_icon.png<br>
> new file mode 100644<br>
> index 0000000000000000000000000000000000000000..ac987e997313910ec3e02a11be8ccda0dfa581c7<br>
> GIT binary patch<br>
> literal 176<br>
> zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b<br>
> z3=G`DAk4@xYmNj^kiEpy*OmPaH$RJ!-UgFp7lA^yo-U3d7N_4%cI0AE;5i(0^Z))B<br>
> z|EBQpgWZ)55eu3H82wmS6TZt#lE`$@S@Cy{aM-usE&4llHh-IQIlXHpO8~cw3TFo|<br>
> P&>#j+S3j3^P6<r_g#|R0<br>
><br>
> literal 0<br>
> HcmV?d00001<br>
><br>
> diff --git a/src/compositor.c b/src/compositor.c<br>
> index 587fded..686a11b 100644<br>
> --- a/src/compositor.c<br>
> +++ b/src/compositor.c<br>
> @@ -374,6 +374,8 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)<br>
> if (1 << output->id & left)<br>
> wl_surface_send_leave(&es->surface.resource, resource);<br>
> }<br>
> +<br>
> + es->compositor->shell_interface.send_output_mask(es);<br>
> }<br>
><br>
> static void<br>
> diff --git a/src/compositor.h b/src/compositor.h<br>
> index e5c579b..a60563a 100644<br>
> --- a/src/compositor.h<br>
> +++ b/src/compositor.h<br>
> @@ -88,6 +88,7 @@ struct weston_shell_interface {<br>
> int (*move)(struct shell_surface *shsurf, struct weston_seat *ws);<br>
> int (*resize)(struct shell_surface *shsurf,<br>
> struct weston_seat *ws, uint32_t edges);<br>
> + void (*send_output_mask)(struct weston_surface *surface);<br>
<br>
</div></div>This doesn't feel right. The weston_shell_interface are for higher<br>
level modules (right now, only the xwm) to call into the shell module,<br>
not for the core to call into. This creates a core dependency on the<br>
shell module, which we don't want. A better approach would be a<br>
weston_surface wl_signal that we fire from the end of<br>
weston_surface_update_transform.<br></blockquote><div><br>Yes, a wl_signal fits much more nicely.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> };<br>
><br>
> diff --git a/src/shell.c b/src/shell.c<br>
> index cccfe66..86f2f6f 100644<br>
> --- a/src/shell.c<br>
> +++ b/src/shell.c<br>
> @@ -97,6 +97,8 @@ struct desktop_shell {<br>
> uint32_t deathstamp;<br>
> } child;<br>
><br>
> + struct wl_resource *surface_data_manager;<br>
> +<br>
> bool locked;<br>
> bool showing_input_panels;<br>
> bool prepare_event_sent;<br>
> @@ -201,6 +203,7 @@ struct shell_surface {<br>
> struct wl_list link;<br>
><br>
> const struct weston_shell_client *client;<br>
> + struct wl_resource *surface_data;<br>
> };<br>
><br>
> struct shell_grab {<br>
> @@ -1396,6 +1399,118 @@ shell_surface_pong(struct wl_client *client, struct wl_resource *resource,<br>
> }<br>
><br>
> static void<br>
> +surface_data_object_destroy(struct wl_resource *resource)<br>
> +{<br>
> + struct shell_surface *shsurf = resource->data;<br>
> +<br>
> + free(resource);<br>
> +<br>
> + if (!shsurf)<br>
> + return;<br>
> +<br>
> + shsurf->surface_data = NULL;<br>
> +}<br>
> +<br>
> +static void<br>
> +surface_data_destroy_handler(struct wl_client *client,<br>
> + struct wl_resource *resource)<br>
> +{<br>
> + wl_resource_destroy(resource);<br>
> +}<br>
> +<br>
> +static const struct surface_data_interface<br>
> + surface_data_implementation = {<br>
> + surface_data_destroy_handler<br>
> +};<br>
> +<br>
> +static int<br>
> +create_surface_data(struct desktop_shell *shell, struct shell_surface *shsurf)<br>
> +{<br>
> + struct wl_resource *surface_data;<br>
> +<br>
> + surface_data = malloc(sizeof *surface_data);<br>
> + if (surface_data == NULL)<br>
> + return -1;<br>
> +<br>
> + surface_data->data = shsurf;<br>
> + surface_data-><a href="http://object.id" target="_blank">object.id</a> = 0;<br>
> + surface_data->object.interface = &surface_data_interface;<br>
> + surface_data->destroy = surface_data_object_destroy;<br>
> + surface_data->object.implementation =<br>
> + (void (**)(void)) &surface_data_implementation;<br>
> + wl_signal_init(&surface_data->destroy_signal);<br>
> +<br>
> + wl_client_add_resource(shell->surface_data_manager->client, surface_data);<br>
> +<br>
> + shsurf->surface_data = surface_data;<br>
> +<br>
> + return 0;<br>
> +}<br>
> +<br>
> +static void<br>
> +send_surface_data_object(struct desktop_shell *shell, struct shell_surface *shsurf)<br>
> +{<br>
> + surface_data_manager_send_surface_object(shell->surface_data_manager,<br>
> + shsurf->surface_data);<br>
> +}<br>
<br>
</div></div>This function doesn't add much, just call<br>
surface_data_manager_send_surface_object directly below? Or call it<br>
from create_surface_data?<br></blockquote><div><br>Yes, that's better.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> +static bool<br>
> +surface_is_window_list_candidate(struct weston_surface *surface,<br>
> + struct shell_surface *out)<br>
> +{<br>
> + struct desktop_shell *shell;<br>
> + struct shell_surface *shsurf;<br>
> +<br>
> + shsurf = get_shell_surface(surface);<br>
> + if (!shsurf)<br>
> + return false;<br>
> +<br>
> + shell = shsurf->shell;<br>
> +<br>
> + if (!shell->surface_data_manager)<br>
> + return false;<br>
> +<br>
> + switch (shsurf->type) {<br>
> + default:<br>
> + case SHELL_SURFACE_TRANSIENT:<br>
> + case SHELL_SURFACE_POPUP:<br>
> + case SHELL_SURFACE_NONE:<br>
> + return false;<br>
> + case SHELL_SURFACE_FULLSCREEN:<br>
> + case SHELL_SURFACE_MAXIMIZED:<br>
> + case SHELL_SURFACE_TOPLEVEL:<br>
> + if (!shsurf->surface_data) {<br>
> + if (create_surface_data(shell, shsurf))<br>
> + return false;<br>
> + send_surface_data_object(shell, shsurf);<br>
> + }<br>
> + *out = *shsurf;<br>
<br>
</div></div>This is copying the entire shsurf by value... you could return NULL<br>
for "not a candidate" or the shsurf pointer for "yes", instead of the<br>
bool, but don't copy the entire struct. But I'd rather just do all<br>
this up front when we create the shell_surface.<br></blockquote><div><br>Right, so I've restructured this significantly so that this is fixed in a cleaner way. We have to wait until shsurf->type is set to create the surface_data object since we need to know the type to decide if it is a window list candidate.<br>
</div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><br>
> + return true;<br>
> + }<br>
> +}<br>
> +<br>
> +static void<br>
> +send_surface_data_output_mask(struct weston_surface *surface)<br>
> +{<br>
> + struct shell_surface shsurf;<br>
> +<br>
> + if (surface_is_window_list_candidate(surface, &shsurf))<br>
> + surface_data_send_output_mask(shsurf.surface_data,<br>
> + surface->output_mask);<br>
> +}<br>
> +<br>
> +static void<br>
> +send_surface_data_title(struct weston_surface *surface)<br>
> +{<br>
> + struct shell_surface shsurf;<br>
> +<br>
> + if (surface_is_window_list_candidate(surface, &shsurf))<br>
<br>
</div>If we create the surface_data up front, we just need to call<br>
get_shell_surface(surface) and then check if shsurf->surface_data is<br>
not NULL.<br></blockquote><div><br>Yes, this works.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + surface_data_send_title(shsurf.surface_data,<br>
> + shsurf.title == NULL ?<br>
> + "Surface" : shsurf.title);<br>
> +}<br>
> +<br>
> +static void<br>
> shell_surface_set_title(struct wl_client *client,<br>
> struct wl_resource *resource, const char *title)<br>
> {<br>
> @@ -1403,6 +1518,7 @@ shell_surface_set_title(struct wl_client *client,<br>
><br>
> free(shsurf->title);<br>
> shsurf->title = strdup(title);<br>
> + send_surface_data_title(shsurf->surface);<br>
> }<br>
><br>
> static void<br>
> @@ -1519,6 +1635,8 @@ set_surface_type(struct shell_surface *shsurf)<br>
> default:<br>
> break;<br>
> }<br>
> +<br>
> + send_surface_data_title(surface);<br>
> }<br>
><br>
> static void<br>
> @@ -1930,6 +2048,8 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {<br>
> static void<br>
> destroy_shell_surface(struct shell_surface *shsurf)<br>
> {<br>
> + if (shsurf->surface_data)<br>
> + surface_data_send_gone(shsurf->surface_data);<br>
> if (shsurf->popup.grab.pointer)<br>
> wl_pointer_end_grab(shsurf->popup.grab.pointer);<br>
><br>
> @@ -2314,6 +2434,20 @@ static const struct desktop_shell_interface desktop_shell_implementation = {<br>
> desktop_shell_set_grab_surface<br>
> };<br>
><br>
> +static void<br>
> +surface_data_send_all_info(struct desktop_shell *shell)<br>
> +{<br>
> + struct weston_surface *surface;<br>
> + struct workspace *ws;<br>
> +<br>
> + ws = get_current_workspace(shell);<br>
> +<br>
> + wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {<br>
<br>
</div></div>And here we'd have to create the surface_data before sending out these<br>
events.<br></blockquote><div><br>Yep.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div><br>
> + send_surface_data_output_mask(surface);<br>
> + send_surface_data_title(surface);<br>
> + }<br>
> +}<br>
> +<br>
> static enum shell_surface_type<br>
> get_shell_surface_type(struct weston_surface *surface)<br>
> {<br>
> @@ -3143,6 +3277,37 @@ bind_desktop_shell(struct wl_client *client,<br>
> }<br>
><br>
> static void<br>
> +unbind_surface_data_manager(struct wl_resource *resource)<br>
> +{<br>
> + struct desktop_shell *shell = resource->data;<br>
> +<br>
> + shell->surface_data_manager = NULL;<br>
> + free(resource);<br>
> +}<br>
> +<br>
> +static void<br>
> +bind_surface_data_manager(struct wl_client *client,<br>
> + void *data, uint32_t version, uint32_t id)<br>
> +{<br>
> + struct desktop_shell *shell = data;<br>
> + struct wl_resource *resource;<br>
> +<br>
> + resource = wl_client_add_object(client, &surface_data_manager_interface,<br>
> + NULL, id, shell);<br>
> +<br>
> + if (client == shell->child.client) {<br>
> + resource->destroy = unbind_surface_data_manager;<br>
> + shell->surface_data_manager = resource;<br>
> + surface_data_send_all_info(shell);<br>
> + return;<br>
> + }<br>
> +<br>
> + wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,<br>
> + "permission to bind desktop_shell denied");<br>
> + wl_resource_destroy(resource);<br>
> +}<br>
> +<br>
> +static void<br>
> screensaver_configure(struct weston_surface *surface, int32_t sx, int32_t sy)<br>
> {<br>
> struct desktop_shell *shell = surface->private;<br>
> @@ -3836,6 +4001,7 @@ module_init(struct weston_compositor *ec)<br>
> ec->shell_interface.set_transient = set_transient;<br>
> ec->shell_interface.move = surface_move;<br>
> ec->shell_interface.resize = surface_resize;<br>
> + ec->shell_interface.send_output_mask = send_surface_data_output_mask;<br>
><br>
> wl_list_init(&shell->screensaver.surfaces);<br>
> wl_list_init(&shell->input_panel.surfaces);<br>
> @@ -3887,6 +4053,10 @@ module_init(struct weston_compositor *ec)<br>
> shell, bind_workspace_manager) == NULL)<br>
> return -1;<br>
><br>
> + if (wl_display_add_global(ec->wl_display, &surface_data_manager_interface,<br>
> + shell, bind_surface_data_manager) == NULL)<br>
> + return -1;<br>
> +<br>
> shell->child.deathstamp = weston_compositor_get_time();<br>
><br>
> loop = wl_display_get_event_loop(ec->wl_display);<br>
> --<br>
> 1.7.11.7<br><br></div></div></blockquote><div><br>Thanks for the review Kristian, I will probably have this set updated and ready soon.<br><br>- Scott <br></div></div><br>