[PATCH weston] desktop-shell: Implement panel window list.
Scott Moreau
oreaus at gmail.com
Mon Oct 1 01:06:48 PDT 2012
The compositor sends surface info as necessary to the wl_shell client, using
a special interface extension. Surfaces are identified by a specific id value.
The value of the id isn't particularly important as long as 1) the value is
unique for each surface 2) the client and compositor use the same id values.
In this implementation, the id is a pointer to a struct weston_surface.
---
I figured I'd submit this as a preliminary proof-of-concept for a panel window
list. It does nothing but build a window list based upon what the compositor
sends and renders it into the panel. The list buttons are fit to send requests
for focus/toggle minimize on click, which is the plan for upcoming patches. The
surface id is sent as a raw pointer value because there is no way to send an
'official' object. That is, the shell is a privileged client that is exclusively
allowed to use this special protocol. The client shouldn't worry about what the
id value is, it should only use it as a unique number to identify a client when
sending requests on its behalf. The compositor will recognize the same id that
it sent, when handling requests for this special interface.
The panel window list semantics are loosely based off of gnome-panel. It is
per-output in the sense that the lists are updated dynamically when moving a
surface to a new output. The panel lists are updated in real time according to
the surface output_mask. This is very much a work in progress. Comments and
ideas are welcome.
One thing I'm not sure about is how to properly clean up, as I do not see a
place where the desktop is destroyed. It seems that many resources are left
unmanaged when weston-desktop-shell client dies, unless I'm missing something.
clients/desktop-shell.c | 421 +++++++++++++++++++++++++++++++++++++++++++--
protocol/desktop-shell.xml | 27 +++
src/compositor.c | 2 +
src/compositor.h | 1 +
src/shell.c | 58 ++++++-
5 files changed, 497 insertions(+), 12 deletions(-)
diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
index 588dc1c..7b37bb9 100644
--- a/clients/desktop-shell.c
+++ b/clients/desktop-shell.c
@@ -52,6 +52,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;
@@ -60,6 +61,13 @@ struct desktop {
};
struct surface {
+ struct wl_list item_list;
+ uint32_t id;
+ uint32_t output_mask;
+ char *title;
+};
+
+struct resize {
void (*configure)(void *data,
struct desktop_shell *desktop_shell,
uint32_t edges, struct window *window,
@@ -67,21 +75,26 @@ struct surface {
};
struct panel {
- struct surface base;
+ struct resize base;
struct window *window;
struct widget *widget;
struct wl_list launcher_list;
+ struct wl_list window_list;
+ uint32_t surface_count;
struct panel_clock *clock;
+ struct rectangle launcher_rect;
+ float window_list_padding;
};
struct background {
- struct surface base;
+ struct resize base;
struct window *window;
struct widget *widget;
};
struct output {
struct wl_output *output;
+ uint32_t id;
struct wl_list link;
struct panel *panel;
@@ -99,6 +112,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;
@@ -303,6 +326,146 @@ panel_launcher_button_handler(struct widget *widget,
}
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_list_item_redraw_handler(struct widget *widget, void *data)
+{
+ cairo_t *cr;
+ cairo_surface_t *surface;
+ struct list_item *item = data;
+ struct rectangle allocation;
+ cairo_text_extents_t extents;
+ cairo_font_extents_t font_extents;
+ int icon_width;
+ unsigned int dots = 3;
+ char title[128];
+ float r, g, b, a;
+
+ widget_get_allocation(widget, &allocation);
+ if (allocation.width == 0)
+ return;
+
+ surface = window_get_surface(item->panel->window);
+ cr = cairo_create(surface);
+
+ if (item->focused) {
+ /* Consider panel color when choosing item highlight color */
+ get_hex_color_rgba(key_panel_color, &r, &b, &g, &a);
+ if (r += 0.1, g += 0.1, b += 0.1, r > 1.0 || g > 1.0 || b > 1.0)
+ cairo_set_source_rgba(cr, 0.6, 0.6, 0.6, 0.75);
+ else
+ cairo_set_source_rgba(cr, r + 0.1, g + 0.1, b + 0.1, 0.75);
+ cairo_move_to(cr, allocation.x, allocation.y);
+ cairo_line_to(cr, allocation.x + allocation.width, allocation.y);
+ cairo_line_to(cr, allocation.x + allocation.width, allocation.y + allocation.height);
+ cairo_line_to(cr, allocation.x, allocation.y + allocation.height);
+ cairo_line_to(cr, allocation.x, allocation.y);
+ cairo_fill(cr);
+ }
+
+ icon_width = cairo_image_surface_get_width(item->icon);
+ if (allocation.width > icon_width * 2) {
+ cairo_set_source_surface(cr, item->icon,
+ allocation.x, allocation.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 > (allocation.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, allocation.x + 10 + icon_width,
+ allocation.y + 3 * (allocation.height >> 2) + 1);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ cairo_show_text(cr, title);
+ cairo_move_to(cr, allocation.x + 9 + icon_width,
+ allocation.y + 3 * (allocation.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 void
clock_func(struct task *task, uint32_t events)
{
struct panel_clock *clock =
@@ -337,7 +500,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);
@@ -417,9 +580,11 @@ panel_resize_handler(struct widget *widget,
struct panel_launcher *launcher;
struct panel *panel = data;
int x, y, w, h;
-
+
x = 10;
y = 16;
+ panel->launcher_rect.x = x;
+ panel->launcher_rect.y = y;
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);
@@ -427,8 +592,10 @@ panel_resize_handler(struct widget *widget,
x, y - h / 2, w + 1, h + 1);
x += w + 10;
}
- h=20;
+ panel->launcher_rect.width = x - panel->launcher_rect.x;
+ panel->launcher_rect.height = h + 1;
w=170;
+ h=20;
if (panel->clock)
widget_set_allocation(panel->clock->widget,
@@ -441,7 +608,7 @@ panel_configure(void *data,
uint32_t edges, struct window *window,
int32_t width, int32_t height)
{
- struct surface *surface = window_get_user_data(window);
+ struct resize *surface = window_get_user_data(window);
struct panel *panel = container_of(surface, struct panel, base);
window_schedule_resize(panel->window, width, 32);
@@ -459,6 +626,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);
@@ -466,7 +634,8 @@ panel_create(struct display *display)
widget_set_redraw_handler(panel->widget, panel_redraw_handler);
widget_set_resize_handler(panel->widget, panel_resize_handler);
widget_set_button_handler(panel->widget, panel_button_handler);
-
+
+ panel->surface_count = 0;
panel_add_clock(panel);
return panel;
@@ -475,6 +644,8 @@ panel_create(struct display *display)
static cairo_surface_t *
load_icon_or_fallback(const char *icon)
{
+ if (!icon)
+ goto fallback;
cairo_surface_t *surface = cairo_image_surface_create_from_png(icon);
cairo_t *cr;
@@ -483,7 +654,7 @@ load_icon_or_fallback(const char *icon)
cairo_surface_destroy(surface);
fprintf(stderr, "ERROR loading icon from file '%s'\n", icon);
-
+fallback:
/* draw fallback icon */
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
20, 20);
@@ -506,6 +677,44 @@ load_icon_or_fallback(const char *icon)
return surface;
}
+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
panel_add_launcher(struct panel *panel, const char *icon, const char *path)
{
@@ -810,7 +1019,7 @@ desktop_shell_configure(void *data,
int32_t width, int32_t height)
{
struct window *window = wl_surface_get_user_data(surface);
- struct surface *s = window_get_user_data(window);
+ struct resize *s = window_get_user_data(window);
s->configure(data, desktop_shell, edges, window, width, height);
}
@@ -879,10 +1088,194 @@ desktop_shell_grab_cursor(void *data,
}
}
+static struct surface*
+create_surface(void)
+{
+ struct surface *surface;
+
+ surface = calloc(1, sizeof *surface);
+
+ if (!surface) {
+ fprintf(stderr, "ERROR: Failed to allocate memory!\n");
+ exit(-1);
+ }
+
+ wl_list_init(&surface->item_list);
+
+ return surface;
+}
+
+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);
+}
+
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+static void
+draw_window_list(struct panel *panel)
+{
+ struct list_item *item;
+ struct rectangle clock_rect;
+ struct rectangle panel_rect;
+ float x, y, w, h;
+ float item_width;
+ struct rectangle window_list_rect;
+
+ /* 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;
+ }
+
+ widget_get_allocation(panel->clock->widget, &clock_rect);
+ widget_get_allocation(panel->widget, &panel_rect);
+
+ window_list_rect.x = panel->launcher_rect.x +
+ panel->launcher_rect.width;
+ window_list_rect.y = 2;
+ window_list_rect.width = panel_rect.width -
+ window_list_rect.x -
+ (clock_rect.width + 20);
+ window_list_rect.height = 28;
+ item_width = ((float) window_list_rect.width /
+ panel->surface_count);
+ panel->window_list_padding = MIN(item_width * 0.1f, 10.0f);
+
+ x = window_list_rect.x + panel->window_list_padding;
+ y = 16;
+ w = MIN(item_width - panel->window_list_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 + panel->window_list_padding;
+ widget_schedule_redraw(item->widget);
+ }
+}
+
+static void
+update_panel_list_items(struct desktop *desktop, struct surface *surface)
+{
+ struct list_item *item, *next;
+ struct output *output;
+ int item_exists;
+
+ /* Make a list item for each panel of the surfaces output mask */
+ wl_list_for_each(output, &desktop->outputs, link) {
+ if ((1 << output->id) & surface->output_mask) {
+ item_exists = 0;
+ wl_list_for_each(item, &output->panel->window_list,
+ link) {
+ wl_list_for_each(next, &surface->item_list,
+ surface_link) {
+ if (next == item &&
+ item->panel == output->panel) {
+ item_exists = 1;
+ break;
+ }
+ }
+ }
+ if (!item_exists) {
+ item = panel_add_list_item(output->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 == output->panel)
+ panel_remove_list_item(item);
+ }
+ }
+ draw_window_list(output->panel);
+ }
+}
+
+static void
+desktop_shell_surface_info(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t id,
+ 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 (id == surface->id) {
+ surface_exists = 1;
+ break;
+ }
+ }
+ if (surface_exists)
+ break;
+ }
+
+ /* Else, create a new surface intance to track it */
+ if (!surface_exists)
+ surface = create_surface();
+
+ surface->id = id;
+ surface->output_mask = output_mask;
+ if (surface->title)
+ free(surface->title);
+ surface->title = strdup(title);
+
+ update_panel_list_items(desktop, surface);
+}
+
+static void
+desktop_shell_surface_destroy(void *data,
+ struct desktop_shell *desktop_shell,
+ uint32_t id)
+{
+ struct desktop *desktop = data;
+ struct list_item *item, *next;
+ struct panel *panel;
+ struct surface *surface;
+ struct output *output;
+
+ 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 (id == surface->id) {
+ /* Free resources and redraw window list */
+ destroy_surface(surface);
+ draw_window_list(output->panel);
+ return;
+ }
+ }
+ }
+}
+
static const struct desktop_shell_listener listener = {
desktop_shell_configure,
desktop_shell_prepare_lock_surface,
- desktop_shell_grab_cursor
+ desktop_shell_grab_cursor,
+ desktop_shell_surface_info,
+ desktop_shell_surface_destroy
};
static struct background *
@@ -941,10 +1334,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
@@ -1011,6 +1406,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);
@@ -1041,6 +1438,8 @@ int main(int argc, char *argv[])
signal(SIGCHLD, sigchild_handler);
+ desktop_shell_get_surface_info(desktop.shell);
+
display_run(desktop.display);
return 0;
diff --git a/protocol/desktop-shell.xml b/protocol/desktop-shell.xml
index 2b6afbd..376d423 100644
--- a/protocol/desktop-shell.xml
+++ b/protocol/desktop-shell.xml
@@ -80,6 +80,33 @@
<entry name="busy" value="11"/>
</enum>
+
+ <request name="get_surface_info">
+ <description summary="request surface list">
+ The wl_shell client can use this request to ask the window manager to
+ send surface_info events for all surfaces. The window manager should
+ send only normal surfaces that can be part of a common window list.
+ </description>
+ </request>
+
+ <event name="surface_info">
+ <description summary="surface information">
+ Surface information sent to a wl_shell client. This information is
+ intended to allow the wl_shell client to build a window list. It
+ should be sent when a wl_shell_surface is created.
+ </description>
+ <arg name="id" type="uint"/>
+ <arg name="output_mask" type="uint"/>
+ <arg name="title" type="string"/>
+ </event>
+
+ <event name="surface_destroy">
+ <description summary="surface destroy notification">
+ This should be sent when a wl_shell_surface is destroyed. The wl_shell
+ client can use this information to keep the window list current.
+ </description>
+ <arg name="id" type="uint"/>
+ </event>
</interface>
<interface name="screensaver" version="1">
diff --git a/src/compositor.c b/src/compositor.c
index 417c508..7afdbdb 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -368,6 +368,8 @@ weston_surface_update_output_mask(struct weston_surface *es, uint32_t mask)
if (1 << output->id & left)
wl_surface_send_leave(&es->surface.resource, resource);
}
+
+ es->compositor->shell_interface.send_info(es);
}
static void
diff --git a/src/compositor.h b/src/compositor.h
index aea4f8d..4304d21 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -89,6 +89,7 @@ struct weston_shell_interface {
int (*move)(struct shell_surface *shsurf, struct weston_seat *ws);
int (*resize)(struct shell_surface *shsurf,
struct weston_seat *ws, uint32_t edges);
+ void (*send_info)(struct weston_surface *surface);
};
diff --git a/src/shell.c b/src/shell.c
index e2715d6..31b15b6 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1392,6 +1392,36 @@ shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
}
static void
+shell_surface_send_info(struct weston_surface *surface)
+{
+ struct shell_surface *shsurf;
+ struct desktop_shell *shell;
+
+ shsurf = get_shell_surface(surface);
+ if (!shsurf)
+ return;
+
+ shell = shsurf->shell;
+
+ switch (shsurf->type) {
+ default:
+ case SHELL_SURFACE_TRANSIENT:
+ case SHELL_SURFACE_POPUP:
+ case SHELL_SURFACE_NONE:
+ return;
+ case SHELL_SURFACE_FULLSCREEN:
+ case SHELL_SURFACE_MAXIMIZED:
+ case SHELL_SURFACE_TOPLEVEL:
+ desktop_shell_send_surface_info(shell->child.desktop_shell,
+ (long unsigned int) surface,
+ surface->output_mask,
+ shsurf->title == NULL ?
+ "Surface" : shsurf->title);
+ return;
+ }
+}
+
+static void
shell_surface_set_title(struct wl_client *client,
struct wl_resource *resource, const char *title)
{
@@ -1399,6 +1429,7 @@ shell_surface_set_title(struct wl_client *client,
free(shsurf->title);
shsurf->title = strdup(title);
+ shell_surface_send_info(shsurf->surface);
}
static void
@@ -1907,6 +1938,8 @@ static const struct wl_shell_surface_interface shell_surface_implementation = {
static void
destroy_shell_surface(struct shell_surface *shsurf)
{
+ desktop_shell_send_surface_destroy(shsurf->shell->child.desktop_shell,
+ (long unsigned int) shsurf->surface);
if (shsurf->popup.grab.pointer)
wl_pointer_end_grab(shsurf->popup.grab.pointer);
@@ -2283,12 +2316,28 @@ desktop_shell_set_grab_surface(struct wl_client *client,
shell->grab_surface = surface_resource->data;
}
+static void
+desktop_shell_surface_info_requested(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ struct weston_surface *surface;
+ struct desktop_shell *shell;
+ struct workspace *ws;
+
+ shell = resource->data;
+ ws = get_current_workspace(shell);
+
+ wl_list_for_each(surface, &ws->layer.surface_list, layer_link)
+ shell_surface_send_info(surface);
+}
+
static const struct desktop_shell_interface desktop_shell_implementation = {
desktop_shell_set_background,
desktop_shell_set_panel,
desktop_shell_set_lock_surface,
desktop_shell_unlock,
- desktop_shell_set_grab_surface
+ desktop_shell_set_grab_surface,
+ desktop_shell_surface_info_requested
};
static enum shell_surface_type
@@ -2915,6 +2964,12 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
default:
ws = get_current_workspace(shell);
wl_list_insert(&ws->layer.surface_list, &surface->layer_link);
+ weston_surface_update_transform(surface);
+ desktop_shell_send_surface_info(shsurf->shell->child.desktop_shell,
+ (long unsigned int) surface,
+ surface->output_mask,
+ shsurf->title == NULL ?
+ "Surface" : shsurf->title);
break;
}
@@ -3723,6 +3778,7 @@ module_init(struct weston_compositor *ec)
ec->shell_interface.set_transient = set_transient;
ec->shell_interface.move = surface_move;
ec->shell_interface.resize = surface_resize;
+ ec->shell_interface.send_info = shell_surface_send_info;
wl_list_init(&shell->screensaver.surfaces);
wl_list_init(&shell->input_panel.surfaces);
--
1.7.11.4
More information about the wayland-devel
mailing list