The compositor sends surface info as necessary to the wl_shell client using the special surface_data interface. This patch uses this information to render a window list in the panel.
--- v3: * Restructured to use the new surface_data interface objects * Split into multiple patches for clarity and easier review * More cleanup clients/desktop-shell.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 415 insertions(+), 3 deletions(-) diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index 5c629cb..5d2c1f7 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 { @@ -52,6 +54,7 @@ struct desktop { struct unlock_dialog *unlock_dialog; struct task unlock_task; struct wl_list outputs; + uint32_t output_count; struct window *grab_window; struct widget *grab_widget; @@ -62,6 +65,7 @@ struct desktop { }; struct surface { + struct wl_list item_list; struct surface_data *surface_data; uint32_t output_mask; char *title; @@ -74,11 +78,19 @@ struct resize { int32_t width, int32_t height); }; +struct rgba { + float r, g, b, a; +}; + struct panel { 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; }; @@ -90,6 +102,7 @@ struct background { struct output { struct wl_output *output; + uint32_t id; struct wl_list link; struct panel *panel; @@ -107,6 +120,16 @@ struct panel_launcher { struct wl_array argv; }; +struct list_item { + struct surface *surface; + struct widget *widget; + struct panel *panel; + cairo_surface_t *icon; + int focused, pressed; + struct wl_list link; + struct wl_list surface_link; +}; + struct panel_clock { struct widget *widget; struct panel *panel; @@ -257,6 +280,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; @@ -345,7 +377,7 @@ panel_clock_redraw_handler(struct widget *widget, void *data) surface = window_get_surface(clock->panel->window); cr = cairo_create(surface); - cairo_select_font_face(cr, "sans", + cairo_select_font_face(cr, "helvetica", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_set_font_size(cr, 14); @@ -419,15 +451,49 @@ panel_button_handler(struct widget *widget, } static void +panel_draw_window_list(struct panel *panel) +{ + struct list_item *item; + float x, y, w, h; + float item_width, padding; + + /* 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 = ((float) panel->window_list_rect.width / + panel->surface_count); + padding = MIN(item_width * 0.1f, 10.0f); + + x = panel->window_list_rect.x + padding; + y = 16; + w = MIN(item_width - padding, 200); + h = 24; + + wl_list_for_each(item, &panel->window_list, link) { + widget_set_allocation(item->widget, x, y - h / 2, w + 1, h + 1); + 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); @@ -435,12 +501,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); + + 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_draw_window_list(panel); } static void @@ -455,6 +534,25 @@ panel_configure(void *data, window_schedule_resize(panel->window, width, 32); } +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); + if (r += 0.2, g += 0.2, b += 0.2, r > 1.0 || g > 1.0 || b > 1.0) { + panel->focused_item.r = 0.6; + panel->focused_item.g = 0.6; + panel->focused_item.b = 0.6; + } else { + panel->focused_item.r = r; + panel->focused_item.g = g; + panel->focused_item.b = b; + } + panel->focused_item.a = 0.75; +} + static struct panel * panel_create(struct display *display) { @@ -467,6 +565,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); @@ -475,6 +574,8 @@ panel_create(struct display *display) 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; @@ -892,18 +993,325 @@ static const struct desktop_shell_listener listener = { desktop_shell_grab_cursor }; +static struct surface* +create_surface(void) +{ + struct surface *surface; + + surface = calloc(1, sizeof *surface); + + if (!surface) { + fprintf(stderr, "ERROR: Failed to allocate memory!\n"); + return NULL; + } + + wl_list_init(&surface->item_list); + + return surface; +} + +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; + unsigned int dots = 3; + 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_move_to(cr, rect.x, rect.y); + cairo_line_to(cr, rect.x + rect.width, rect.y); + cairo_line_to(cr, rect.x + rect.width, rect.y + rect.height); + cairo_line_to(cr, rect.x, rect.y + rect.height); + cairo_line_to(cr, rect.x, rect.y); + cairo_fill(cr); + } + + icon_width = cairo_image_surface_get_width(item->icon); + if (rect.width > icon_width * 2) { + cairo_set_source_surface(cr, item->icon, + rect.x, rect.y); + cairo_paint(cr); + } else + icon_width = 0; + + strcpy(title, item->surface->title); + cairo_select_font_face(cr, "helvetica", + 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 - (10 + icon_width))) { + 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 */ + 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 + 10 + icon_width, + 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 + 9 + icon_width, + rect.y + 3 * (rect.height >> 2)); + if (item->focused) + 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->focused = 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->focused = 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_add_list_item(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_remove_list_item(struct list_item *item) +{ + item->panel->surface_count--; + wl_list_remove(&item->link); + wl_list_remove(&item->surface_link); + widget_destroy(item->widget); + free(item); +} + +static void +destroy_surface(struct surface *surface) +{ + struct list_item *item, *next; + + wl_list_for_each_safe(item, next, &surface->item_list, surface_link) + panel_remove_list_item(item); + + free(surface->title); + free(surface); +} + +static int +panel_list_item_exists(struct panel *panel, + struct surface *surface, + struct list_item *item) +{ + struct list_item *next; + int exists = 0; + + wl_list_for_each(next, &surface->item_list, surface_link) { + if (item == next && item->panel == panel) { + exists = 1; + break; + } + } + + return exists; +} + +static void +output_update_window_list(struct output *output, struct surface *surface) +{ + struct list_item *item, *next; + struct panel *panel; + int item_exists; + + panel = output->panel; + + if ((1 << output->id) & surface->output_mask) { + item_exists = 0; + wl_list_for_each(item, &panel->window_list, link) { + if (panel_list_item_exists(panel, surface, item)) { + item_exists = 1; + break; + } + } + if (!item_exists) { + /* TODO: Use a real icon instead of + * passing NULL for the fallback */ + item = panel_add_list_item(panel, NULL, 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_remove_list_item(item); + } + } +} + +static void +panel_update_list_items(struct desktop *desktop, struct surface *surface) +{ + struct output *output; + + /* Make a list item for each panel of the surfaces output mask */ + wl_list_for_each(output, &desktop->outputs, link) { + output_update_window_list(output, surface); + panel_draw_window_list(output->panel); + } +} + static void surface_data_receive_info(void *data, struct surface_data *surface_data, uint32_t output_mask, const char *title) { + struct desktop *desktop; + struct surface *surface; + struct list_item *item; + struct output *output; + int surface_exists = 0; + + desktop = data; + + /* If this surface is already in our list, prepare to update its info */ + wl_list_for_each(output, &desktop->outputs, link) { + wl_list_for_each(item, &output->panel->window_list, link) { + surface = item->surface; + if (surface_data == surface->surface_data) { + surface_exists = 1; + break; + } + } + if (surface_exists) + break; + } + + /* Else, create a new surface intance to track it */ + if (!surface_exists) + surface = create_surface(); + + if (!surface) + return; + + surface->surface_data = surface_data; + surface->output_mask = output_mask; + if (surface->title) + free(surface->title); + surface->title = strdup(title); + + panel_update_list_items(desktop, surface); } static void surface_data_destroy_handler(void *data, struct surface_data *surface_data) { + struct desktop *desktop = data; + struct list_item *item, *next; + struct panel *panel; + struct surface *surface; + struct output *output; + surface_data_destroy_request(surface_data); + + wl_list_for_each(output, &desktop->outputs, link) { + panel = output->panel; + wl_list_for_each_safe(item, next, &panel->window_list, link) { + surface = item->surface; + if (surface_data == surface->surface_data) { + /* Free resources and redraw window list */ + destroy_surface(surface); + panel_draw_window_list(output->panel); + return; + } + } + } } static const struct surface_data_listener surface_data_listener = { @@ -980,10 +1388,12 @@ create_output(struct desktop *desktop, uint32_t id) if (!output) return; + output->id = desktop->output_count++; + output->output = wl_display_bind(display_get_display(desktop->display), id, &wl_output_interface); - wl_list_insert(&desktop->outputs, &output->link); + wl_list_insert(desktop->outputs.prev, &output->link); } static void @@ -1056,6 +1466,8 @@ int main(int argc, char *argv[]) return -1; } + desktop.output_count = 0; + display_set_user_data(desktop.display, &desktop); wl_display_add_global_listener(display_get_display(desktop.display), global_handler, &desktop); -- 1.7.11.4 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel