Hi, On 3 July 2013 15:40, Giulio Camuffo <giuliocamu...@gmail.com> wrote: > This is cool, but are we sure we want it?
A very valid question. :) > From the "Clarifying scope and goals for weston" mail sent by Kristian [1]: > [...] > > It seems to me this Exposay is out of scope, and adds a quite big amount of > code > to shell.c, which is already quite big and complex to understand. If > anything, i think > it should go in a separate file. I think going in a separate file is a completely fair shout. The motivation behind adding it wasn't to add functionality per se: being totally honest, it's not a very good implementation of the idea. Doing it properly requires a real packing/layout system, which we just don't have, so rather than add something comprehensive, I just hardcoded a bunch of values to have it work well enough for the general case. The reason I added it was as a demonstration of 2D compositing functionality. Saying 'see these windows displayed normally' isn't very compelling; for the Raspberry Pi work, we wanted to show what you could achieve with 2D compositing engines - something impossible with X. This is a lot more portable than RPi though, as even Haswell is gaining more overlays, and the DRM plane API is more than capable of handling move & scale transforms, which is all we use here. So yeah, on the face of it it seems like it shouldn't be merged - and I agree that cramming more functionality into desktop-shell for the sake of it is a fools' game - but it's there as a tech demo rather than anything else. Cheers, Daniel > [1] > http://lists.freedesktop.org/archives/wayland-devel/2013-April/008659.html > > > 2013/7/3 Tomeu Vizoso <to...@tomeuvizoso.net> >> >> From: Daniel Stone <dan...@fooishbar.org> >> >> Exposay provides window overview functions which, when a key which >> produces the binding modifier is pressed on its own, scales all >> currently-open windows down to be shown overlaid on the desktop, >> providing keyboard and mouse navigation to be able to switch window >> focus. >> >> Signed-off-by: Daniel Stone <dan...@fooishbar.org> >> --- >> src/shell.c | 574 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 574 insertions(+) >> >> diff --git a/src/shell.c b/src/shell.c >> index 80b3e8b..cadcb55 100644 >> --- a/src/shell.c >> +++ b/src/shell.c >> @@ -1,6 +1,7 @@ >> /* >> * Copyright © 2010-2012 Intel Corporation >> * Copyright © 2011-2012 Collabora, Ltd. >> + * Copyright © 2013 Raspberry Pi Foundation >> * >> * Permission to use, copy, modify, distribute, and sell this software >> and >> * its documentation for any purpose is hereby granted without fee, >> provided >> @@ -57,6 +58,19 @@ enum fade_type { >> FADE_OUT >> }; >> >> +enum exposay_target_state { >> + EXPOSAY_TARGET_OVERVIEW, /* show all windows */ >> + EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */ >> + EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */ >> +}; >> + >> +enum exposay_layout_state { >> + EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */ >> + EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */ >> + EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */ >> + EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all >> windows */ >> +}; >> + >> struct focus_state { >> struct weston_seat *seat; >> struct workspace *ws; >> @@ -184,6 +198,34 @@ struct desktop_shell { >> struct wl_event_source *startup_timer; >> } fade; >> >> + struct exposay { >> + /* XXX: Make these exposay_surfaces. */ >> + struct weston_surface *focus_prev; >> + struct weston_surface *focus_current; >> + struct weston_surface *clicked; >> + struct workspace *workspace; >> + struct weston_seat *seat; >> + struct wl_list surface_list; >> + >> + struct weston_keyboard_grab grab_kbd; >> + struct weston_pointer_grab grab_ptr; >> + >> + enum exposay_target_state state_target; >> + enum exposay_layout_state state_cur; >> + int in_flight; /* number of animations still running */ >> + >> + int num_surfaces; >> + int grid_size; >> + int surface_size; >> + >> + int hpadding_outer; >> + int vpadding_outer; >> + int padding_inner; >> + >> + int row_current; >> + int column_current; >> + } exposay; >> + >> uint32_t binding_modifier; >> enum animation_type win_animation_type; >> enum animation_type focus_animation_type; >> @@ -4289,6 +4331,533 @@ switcher_binding(struct weston_seat *seat, >> uint32_t time, uint32_t key, >> switcher_next(switcher); >> } >> >> +struct exposay_surface { >> + struct desktop_shell *shell; >> + struct weston_surface *surface; >> + struct wl_list link; >> + >> + int x; >> + int y; >> + int width; >> + int height; >> + double scale; >> + >> + int row; >> + int column; >> + >> + /* The animations only apply a transformation for their own >> lifetime, >> + * and don't have an option to indefinitely maintain the >> + * transformation in a steady state - so, we apply our own once >> the >> + * animation has finished. */ >> + struct weston_transform transform; >> +}; >> + >> +static void exposay_set_state(struct desktop_shell *shell, >> + enum exposay_target_state state, >> + struct weston_seat *seat); >> +static void exposay_check_state(struct desktop_shell *shell); >> + >> +static void >> +exposay_in_flight_inc(struct desktop_shell *shell) >> +{ >> + shell->exposay.in_flight++; >> +} >> + >> +static void >> +exposay_in_flight_dec(struct desktop_shell *shell) >> +{ >> + if (--shell->exposay.in_flight > 0) >> + return; >> + >> + exposay_check_state(shell); >> +} >> + >> +static void >> +exposay_animate_in_done(struct weston_surface_animation *animation, void >> *data) >> +{ >> + struct exposay_surface *esurface = data; >> + >> + wl_list_insert(&esurface->surface->geometry.transformation_list, >> + &esurface->transform.link); >> + weston_matrix_init(&esurface->transform.matrix); >> + weston_matrix_scale(&esurface->transform.matrix, >> + esurface->scale, esurface->scale, 1.0f); >> + weston_matrix_translate(&esurface->transform.matrix, >> + esurface->x - >> esurface->surface->geometry.x, >> + esurface->y - >> esurface->surface->geometry.y, >> + 0); >> + >> + weston_surface_geometry_dirty(esurface->surface); >> + weston_compositor_schedule_repaint(esurface->surface->compositor); >> + >> + exposay_in_flight_dec(esurface->shell); >> +} >> + >> +static void >> +exposay_animate_in(struct exposay_surface *esurface) >> +{ >> + exposay_in_flight_inc(esurface->shell); >> + >> + weston_move_scale_run(esurface->surface, >> + esurface->x - esurface->surface->geometry.x, >> + esurface->y - esurface->surface->geometry.y, >> + 1.0, esurface->scale, 0, >> + exposay_animate_in_done, esurface); >> +} >> + >> +static void >> +exposay_animate_out_done(struct weston_surface_animation *animation, void >> *data) >> +{ >> + struct exposay_surface *esurface = data; >> + struct desktop_shell *shell = esurface->shell; >> + >> + wl_list_remove(&esurface->link); >> + free(esurface); >> + >> + exposay_in_flight_dec(shell); >> +} >> + >> +static void >> +exposay_animate_out(struct exposay_surface *esurface) >> +{ >> + exposay_in_flight_inc(esurface->shell); >> + >> + /* Remove the static transformation set up by >> + * exposay_transform_in_done(). */ >> + wl_list_remove(&esurface->transform.link); >> + weston_surface_geometry_dirty(esurface->surface); >> + >> + weston_move_scale_run(esurface->surface, >> + esurface->x - esurface->surface->geometry.x, >> + esurface->y - esurface->surface->geometry.y, >> + 1.0, esurface->scale, 1, >> + exposay_animate_out_done, esurface); >> +} >> + >> +static void >> +exposay_highlight_surface(struct desktop_shell *shell, >> + struct exposay_surface *esurface) >> +{ >> + struct weston_surface *ws = NULL; >> + >> + if (esurface) { >> + shell->exposay.row_current = esurface->row; >> + shell->exposay.column_current = esurface->column; >> + ws = esurface->surface; >> + } >> + >> + animate_focus_change(shell, shell->exposay.workspace, >> + shell->exposay.focus_current, ws); >> + shell->exposay.focus_current = ws; >> +} >> + >> +static int >> +exposay_is_animating(struct desktop_shell *shell) >> +{ >> + if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE || >> + shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW) >> + return 0; >> + >> + return (shell->exposay.in_flight > 0); >> +} >> + >> +static void >> +exposay_pick(struct desktop_shell *shell, int x, int y) >> +{ >> + struct exposay_surface *esurface; >> + >> + if (exposay_is_animating(shell)) >> + return; >> + >> + wl_list_for_each(esurface, &shell->exposay.surface_list, link) { >> + if (x < esurface->x || x > esurface->x + esurface->width) >> + continue; >> + if (y < esurface->y || y > esurface->y + esurface->height) >> + continue; >> + >> + exposay_highlight_surface(shell, esurface); >> + return; >> + } >> +} >> + >> +/* Pretty lame layout for now; just tries to make a square. Should take >> + * aspect ratio into account really. Also needs to be notified of >> surface >> + * addition and removal and adjust layout/animate accordingly. */ >> +static enum exposay_layout_state >> +exposay_layout(struct desktop_shell *shell) >> +{ >> + struct workspace *workspace = shell->exposay.workspace; >> + struct weston_compositor *compositor = shell->compositor; >> + struct weston_output *output = get_default_output(compositor); >> + struct weston_surface *ws; >> + struct exposay_surface *esurface; >> + int w, h; >> + int i; >> + int last_row_removed = 0; >> + >> + wl_list_init(&shell->exposay.surface_list); >> + >> + shell->exposay.num_surfaces = 0; >> + wl_list_for_each(ws, &workspace->layer.surface_list, layer_link) { >> + if (!get_shell_surface(ws)) >> + continue; >> + shell->exposay.num_surfaces++; >> + } >> + >> + if (shell->exposay.num_surfaces == 0) { >> + shell->exposay.grid_size = 0; >> + shell->exposay.hpadding_outer = 0; >> + shell->exposay.vpadding_outer = 0; >> + shell->exposay.padding_inner = 0; >> + shell->exposay.surface_size = 0; >> + return EXPOSAY_LAYOUT_OVERVIEW; >> + } >> + >> + /* Lay the grid out as square as possible, losing surfaces from >> the >> + * bottom row if required. Start with fixed padding of a 10% >> margin >> + * around the outside and 80px internal padding between surfaces, >> and >> + * maximise the area made available to surfaces after this, but >> only >> + * to a maximum of 1/3rd the total output size. >> + * >> + * If we can't make a square grid, add one extra row at the bottom >> + * which will have a smaller number of columns. >> + * >> + * XXX: Surely there has to be a better way to express this maths, >> + * right?! >> + */ >> + shell->exposay.grid_size = >> floor(sqrtf(shell->exposay.num_surfaces)); >> + if (pow(shell->exposay.grid_size, 2) != >> shell->exposay.num_surfaces) >> + shell->exposay.grid_size++; >> + last_row_removed = pow(shell->exposay.grid_size, 2) - >> shell->exposay.num_surfaces; >> + >> + shell->exposay.hpadding_outer = (output->width / 10); >> + shell->exposay.vpadding_outer = (output->height / 10); >> + shell->exposay.padding_inner = 80; >> + >> + w = output->width - (shell->exposay.hpadding_outer * 2); >> + w -= shell->exposay.padding_inner * (shell->exposay.grid_size - >> 1); >> + w /= shell->exposay.grid_size; >> + >> + h = output->height - (shell->exposay.vpadding_outer * 2); >> + h -= shell->exposay.padding_inner * (shell->exposay.grid_size - >> 1); >> + h /= shell->exposay.grid_size; >> + >> + shell->exposay.surface_size = (w < h) ? w : h; >> + if (shell->exposay.surface_size > (output->width / 2)) >> + shell->exposay.surface_size = output->width / 2; >> + if (shell->exposay.surface_size > (output->height / 2)) >> + shell->exposay.surface_size = output->height / 2; >> + >> + i = 0; >> + wl_list_for_each(ws, &workspace->layer.surface_list, layer_link) { >> + int pad; >> + >> + pad = shell->exposay.surface_size + >> shell->exposay.padding_inner; >> + >> + if (!get_shell_surface(ws)) >> + continue; >> + >> + esurface = malloc(sizeof(*esurface)); >> + if (!esurface) { >> + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, >> + shell->exposay.seat); >> + break; >> + } >> + >> + wl_list_insert(&shell->exposay.surface_list, >> &esurface->link); >> + esurface->shell = shell; >> + esurface->surface = ws; >> + >> + esurface->row = i / shell->exposay.grid_size; >> + esurface->column = i % shell->exposay.grid_size; >> + >> + esurface->x = shell->exposay.hpadding_outer; >> + esurface->x += pad * esurface->column; >> + esurface->y = shell->exposay.vpadding_outer; >> + esurface->y += pad * esurface->row; >> + >> + if (esurface->row == shell->exposay.grid_size - 1) >> + esurface->x += (shell->exposay.surface_size + >> shell->exposay.padding_inner) * last_row_removed / 2; >> + >> + if (ws->geometry.width > ws->geometry.height) >> + esurface->scale = shell->exposay.surface_size / >> (float) ws->geometry.width; >> + else >> + esurface->scale = shell->exposay.surface_size / >> (float) ws->geometry.height; >> + esurface->width = ws->geometry.width * esurface->scale; >> + esurface->height = ws->geometry.height * esurface->scale; >> + >> + if (shell->exposay.focus_current == esurface->surface) >> + exposay_highlight_surface(shell, esurface); >> + >> + exposay_animate_in(esurface); >> + >> + i++; >> + } >> + >> + weston_compositor_schedule_repaint(shell->compositor); >> + >> + return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW; >> +} >> + >> +static void >> +exposay_motion(struct weston_pointer_grab *grab, uint32_t time) >> +{ >> + struct desktop_shell *shell = >> + container_of(grab, struct desktop_shell, >> exposay.grab_ptr); >> + >> + exposay_pick(shell, >> + wl_fixed_to_int(grab->pointer->x), >> + wl_fixed_to_int(grab->pointer->y)); >> +} >> + >> +static void >> +exposay_button(struct weston_pointer_grab *grab, uint32_t time, uint32_t >> button, >> + uint32_t state_w) >> +{ >> + struct desktop_shell *shell = >> + container_of(grab, struct desktop_shell, >> exposay.grab_ptr); >> + struct weston_seat *seat = grab->pointer->seat; >> + enum wl_pointer_button_state state = state_w; >> + >> + if (button != BTN_LEFT) >> + return; >> + >> + /* Store the surface we clicked on, and don't do anything if we >> end up >> + * releasing on a different surface. */ >> + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { >> + shell->exposay.clicked = shell->exposay.focus_current; >> + return; >> + } >> + >> + if (shell->exposay.focus_current == shell->exposay.clicked) >> + exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); >> + else >> + shell->exposay.clicked = NULL; >> +} >> + >> +static const struct weston_pointer_grab_interface exposay_ptr_grab = { >> + noop_grab_focus, >> + exposay_motion, >> + exposay_button, >> +}; >> + >> +static int >> +exposay_maybe_move(struct desktop_shell *shell, int row, int column) >> +{ >> + struct exposay_surface *esurface; >> + >> + wl_list_for_each(esurface, &shell->exposay.surface_list, link) { >> + if (esurface->row != row || esurface->column != column) >> + continue; >> + >> + exposay_highlight_surface(shell, esurface); >> + return 1; >> + } >> + >> + return 0; >> +} >> + >> +static void >> +exposay_key(struct weston_keyboard_grab *grab, uint32_t time, uint32_t >> key, >> + uint32_t state_w) >> +{ >> + struct weston_seat *seat = grab->keyboard->seat; >> + struct desktop_shell *shell = >> + container_of(grab, struct desktop_shell, >> exposay.grab_kbd); >> + enum wl_keyboard_key_state state = state_w; >> + >> + if (state != WL_KEYBOARD_KEY_STATE_RELEASED) >> + return; >> + >> + switch (key) { >> + case KEY_ESC: >> + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); >> + break; >> + case KEY_ENTER: >> + exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); >> + break; >> + case KEY_UP: >> + exposay_maybe_move(shell, shell->exposay.row_current - 1, >> + shell->exposay.column_current); >> + break; >> + case KEY_DOWN: >> + /* Special case for trying to move to the bottom row when >> it >> + * has fewer items than all the others. */ >> + if (!exposay_maybe_move(shell, shell->exposay.row_current >> + 1, >> + shell->exposay.column_current) && >> + shell->exposay.row_current < (shell->exposay.grid_size >> - 1)) { >> + exposay_maybe_move(shell, >> shell->exposay.row_current + 1, >> + (shell->exposay.num_surfaces % >> + shell->exposay.grid_size) - >> 1); >> + } >> + break; >> + case KEY_LEFT: >> + exposay_maybe_move(shell, shell->exposay.row_current, >> + shell->exposay.column_current - 1); >> + break; >> + case KEY_RIGHT: >> + exposay_maybe_move(shell, shell->exposay.row_current, >> + shell->exposay.column_current + 1); >> + break; >> + case KEY_TAB: >> + /* Try to move right, then down (and to the leftmost >> column), >> + * then if all else fails, to the top left. */ >> + if (!exposay_maybe_move(shell, shell->exposay.row_current, >> + shell->exposay.column_current + 1) >> && >> + !exposay_maybe_move(shell, shell->exposay.row_current >> + 1, 0)) >> + exposay_maybe_move(shell, 0, 0); >> + break; >> + default: >> + break; >> + } >> +} >> + >> +static void >> +exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial, >> + uint32_t mods_depressed, uint32_t mods_latched, >> + uint32_t mods_locked, uint32_t group) >> +{ >> +} >> + >> +static const struct weston_keyboard_grab_interface exposay_kbd_grab = { >> + exposay_key, >> + exposay_modifier, >> +}; >> + >> +/** >> + * Called when the transition from overview -> inactive has completed. >> + */ >> +static enum exposay_layout_state >> +exposay_set_inactive(struct desktop_shell *shell) >> +{ >> + struct weston_seat *seat = shell->exposay.seat; >> + >> + weston_keyboard_end_grab(seat->keyboard); >> + weston_pointer_end_grab(seat->pointer); >> + if (seat->keyboard->input_method_resource) >> + seat->keyboard->grab = &seat->keyboard->input_method_grab; >> + >> + return EXPOSAY_LAYOUT_INACTIVE; >> +} >> + >> +/** >> + * Begins the transition from overview to inactive. */ >> +static enum exposay_layout_state >> +exposay_transition_inactive(struct desktop_shell *shell, int >> switch_focus) >> +{ >> + struct exposay_surface *esurface; >> + >> + /* Call activate() before we start the animations to avoid >> + * animating back the old state and then immediately transitioning >> + * to the new. */ >> + if (switch_focus && shell->exposay.focus_current) >> + activate(shell, shell->exposay.focus_current, >> + shell->exposay.seat); >> + else if (shell->exposay.focus_prev) >> + activate(shell, shell->exposay.focus_prev, >> + shell->exposay.seat); >> + >> + wl_list_for_each(esurface, &shell->exposay.surface_list, link) >> + exposay_animate_out(esurface); >> + weston_compositor_schedule_repaint(shell->compositor); >> + >> + return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE; >> +} >> + >> +static enum exposay_layout_state >> +exposay_transition_active(struct desktop_shell *shell) >> +{ >> + struct weston_seat *seat = shell->exposay.seat; >> + >> + shell->exposay.workspace = get_current_workspace(shell); >> + shell->exposay.focus_prev = seat->keyboard->focus; >> + shell->exposay.focus_current = seat->keyboard->focus; >> + shell->exposay.clicked = NULL; >> + wl_list_init(&shell->exposay.surface_list); >> + >> + lower_fullscreen_layer(shell); >> + shell->exposay.grab_kbd.interface = &exposay_kbd_grab; >> + weston_keyboard_start_grab(seat->keyboard, >> + &shell->exposay.grab_kbd); >> + weston_keyboard_set_focus(seat->keyboard, NULL); >> + >> + shell->exposay.grab_ptr.interface = &exposay_ptr_grab; >> + weston_pointer_start_grab(seat->pointer, >> + &shell->exposay.grab_ptr); >> + weston_pointer_set_focus(seat->pointer, NULL, >> + seat->pointer->x, seat->pointer->y); >> + >> + return exposay_layout(shell); >> +} >> + >> +static void >> +exposay_check_state(struct desktop_shell *shell) >> +{ >> + enum exposay_layout_state state_new = shell->exposay.state_cur; >> + int do_switch = 0; >> + >> + /* Don't do anything whilst animations are running, just store up >> + * target state changes and only act on them when the animations >> have >> + * completed. */ >> + if (exposay_is_animating(shell)) >> + return; >> + >> + switch (shell->exposay.state_target) { >> + case EXPOSAY_TARGET_OVERVIEW: >> + switch (shell->exposay.state_cur) { >> + case EXPOSAY_LAYOUT_OVERVIEW: >> + goto out; >> + case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW: >> + state_new = EXPOSAY_LAYOUT_OVERVIEW; >> + break; >> + default: >> + state_new = exposay_transition_active(shell); >> + break; >> + } >> + break; >> + >> + case EXPOSAY_TARGET_SWITCH: >> + do_switch = 1; /* fallthrough */ >> + case EXPOSAY_TARGET_CANCEL: >> + switch (shell->exposay.state_cur) { >> + case EXPOSAY_LAYOUT_INACTIVE: >> + goto out; >> + case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE: >> + state_new = exposay_set_inactive(shell); >> + break; >> + default: >> + state_new = exposay_transition_inactive(shell, >> do_switch); >> + break; >> + } >> + >> + break; >> + } >> + >> +out: >> + shell->exposay.state_cur = state_new; >> +} >> + >> +static void >> +exposay_set_state(struct desktop_shell *shell, enum exposay_target_state >> state, >> + struct weston_seat *seat) >> +{ >> + shell->exposay.state_target = state; >> + shell->exposay.seat = seat; >> + exposay_check_state(shell); >> +} >> + >> +static void >> +exposay_binding(struct weston_seat *seat, enum weston_keyboard_modifier >> modifier, >> + void *data) >> +{ >> + struct desktop_shell *shell = data; >> + >> + if (shell->exposay.state_target == EXPOSAY_TARGET_OVERVIEW) >> + exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); >> + else >> + exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, seat); >> +} >> + >> static void >> backlight_binding(struct weston_seat *seat, uint32_t time, uint32_t key, >> void *data) >> @@ -4731,6 +5300,8 @@ shell_add_bindings(struct weston_compositor *ec, >> struct desktop_shell *shell) >> >> workspace_move_surface_down_binding, >> shell); >> >> + weston_compositor_add_modifier_binding(ec, mod, exposay_binding, >> shell); >> + >> /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ >> if (shell->workspaces.num > 1) { >> num_workspace_bindings = shell->workspaces.num; >> @@ -4802,6 +5373,9 @@ module_init(struct weston_compositor *ec, >> >> shell_configuration(shell); >> >> + shell->exposay.state_cur = EXPOSAY_LAYOUT_INACTIVE; >> + shell->exposay.state_target = EXPOSAY_TARGET_CANCEL; >> + >> for (i = 0; i < shell->workspaces.num; i++) { >> pws = wl_array_add(&shell->workspaces.array, sizeof *pws); >> if (pws == NULL) >> -- >> 1.8.3.1 >> >> _______________________________________________ >> wayland-devel mailing list >> wayland-devel@lists.freedesktop.org >> http://lists.freedesktop.org/mailman/listinfo/wayland-devel > > > > _______________________________________________ > wayland-devel mailing list > wayland-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/wayland-devel > _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel