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>