[PATCH 2/2 weston v3] desktop-shell: Implement panel window list.
Scott Moreau
oreaus at gmail.com
Tue Oct 2 23:20:17 PDT 2012
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
More information about the wayland-devel
mailing list