summaryrefslogtreecommitdiff
path: root/desktop-shell/shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'desktop-shell/shell.c')
-rw-r--r--desktop-shell/shell.c838
1 files changed, 582 insertions, 256 deletions
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index c2755433..466ea937 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -56,13 +56,6 @@ struct focus_state {
struct wl_listener surface_destroy_listener;
};
-struct shell_output {
- struct desktop_shell *shell;
- struct weston_output *output;
- struct wl_listener destroy_listener;
- struct wl_list link;
-};
-
enum shell_surface_type {
SHELL_SURFACE_NONE,
SHELL_SURFACE_TOPLEVEL,
@@ -70,11 +63,6 @@ enum shell_surface_type {
SHELL_SURFACE_XWAYLAND
};
-struct ping_timer {
- struct wl_event_source *source;
- uint32_t serial;
-};
-
/*
* Surface stacking and ordering.
*
@@ -84,9 +72,9 @@ struct ping_timer {
* in the following order (top-most first):
* • Lock layer (only ever displayed on its own)
* • Cursor layer
+ * • Input panel layer
* • Fullscreen layer
* • Panel layer
- * • Input panel layer
* • Workspace layers
* • Background layer
*
@@ -121,6 +109,8 @@ struct shell_surface {
struct weston_view *view;
int32_t last_width, last_height;
struct wl_listener surface_destroy_listener;
+ struct wl_listener resource_destroy_listener;
+
struct weston_surface *parent;
struct wl_list children_list; /* child surfaces of this one */
struct wl_list children_link; /* sibling surfaces of this one */
@@ -134,6 +124,7 @@ struct shell_surface {
bool saved_size_valid;
bool saved_rotation_valid;
int unresponsive, grabbed;
+ uint32_t resize_edges;
struct {
struct weston_transform transform;
@@ -159,8 +150,6 @@ struct shell_surface {
struct weston_view *black_view;
} fullscreen;
- struct ping_timer *ping_timer;
-
struct weston_transform workspace_transform;
struct weston_output *fullscreen_output;
@@ -174,8 +163,11 @@ struct shell_surface {
bool maximized;
bool fullscreen;
bool relative;
- } state, next_state; /* surface states */
+ } state, next_state, requested_state; /* surface states */
bool state_changed;
+ bool state_requested;
+
+ int focus_count;
};
struct shell_grab {
@@ -214,6 +206,7 @@ struct rotate_grab {
struct shell_seat {
struct weston_seat *seat;
struct wl_listener seat_destroy_listener;
+ struct weston_surface *focused_surface;
struct {
struct weston_pointer_grab grab;
@@ -223,6 +216,16 @@ struct shell_seat {
} popup_grab;
};
+struct shell_client {
+ struct wl_resource *resource;
+ struct wl_client *client;
+ struct desktop_shell *shell;
+ struct wl_listener destroy_listener;
+ struct wl_event_source *ping_timer;
+ uint32_t ping_serial;
+ int unresponsive;
+};
+
void
set_alpha_if_fullscreen(struct shell_surface *shsurf)
{
@@ -230,6 +233,9 @@ set_alpha_if_fullscreen(struct shell_surface *shsurf)
shsurf->fullscreen.black_view->alpha = 0.25;
}
+static struct shell_client *
+get_shell_client(struct wl_client *client);
+
static struct desktop_shell *
shell_surface_get_shell(struct shell_surface *shsurf);
@@ -344,6 +350,7 @@ shell_grab_end(struct shell_grab *grab)
if (grab->shsurf) {
wl_list_remove(&grab->shsurf_destroy_listener.link);
grab->shsurf->grabbed = 0;
+ grab->shsurf->resize_edges = 0;
}
weston_pointer_end_grab(grab->grab.pointer);
@@ -1688,6 +1695,7 @@ surface_resize(struct shell_surface *shsurf,
surface_subsurfaces_boundingbox(shsurf->surface, NULL, NULL,
&resize->width, &resize->height);
+ shsurf->resize_edges = edges;
shell_grab_start(&resize->base, &resize_grab_interface, shsurf,
seat->pointer, edges);
@@ -1728,9 +1736,6 @@ shell_surface_resize(struct wl_client *client, struct wl_resource *resource,
}
static void
-end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer);
-
-static void
busy_cursor_grab_focus(struct weston_pointer_grab *base)
{
struct shell_grab *grab = (struct shell_grab *) base;
@@ -1742,8 +1747,10 @@ busy_cursor_grab_focus(struct weston_pointer_grab *base)
pointer->x, pointer->y,
&sx, &sy);
- if (!grab->shsurf || grab->shsurf->surface != view->surface)
- end_busy_cursor(grab->shsurf, pointer);
+ if (!grab->shsurf || grab->shsurf->surface != view->surface) {
+ shell_grab_end(grab);
+ free(grab);
+ }
}
static void
@@ -1791,6 +1798,9 @@ set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
{
struct shell_grab *grab;
+ if (pointer->grab->interface == &busy_cursor_grab_interface)
+ return;
+
grab = malloc(sizeof *grab);
if (!grab)
return;
@@ -1800,80 +1810,97 @@ set_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
}
static void
-end_busy_cursor(struct shell_surface *shsurf, struct weston_pointer *pointer)
+end_busy_cursor(struct weston_compositor *compositor, struct wl_client *client)
{
- struct shell_grab *grab = (struct shell_grab *) pointer->grab;
+ struct shell_grab *grab;
+ struct weston_seat *seat;
- if (grab->grab.interface == &busy_cursor_grab_interface &&
- grab->shsurf == shsurf) {
- shell_grab_end(grab);
- free(grab);
+ wl_list_for_each(seat, &compositor->seat_list, link) {
+ if (seat->pointer == NULL)
+ continue;
+
+ grab = (struct shell_grab *) seat->pointer->grab;
+ if (grab->grab.interface == &busy_cursor_grab_interface &&
+ wl_resource_get_client(grab->shsurf->resource) == client) {
+ shell_grab_end(grab);
+ free(grab);
+ }
}
}
static void
-ping_timer_destroy(struct shell_surface *shsurf)
-{
- if (!shsurf || !shsurf->ping_timer)
- return;
-
- if (shsurf->ping_timer->source)
- wl_event_source_remove(shsurf->ping_timer->source);
-
- free(shsurf->ping_timer);
- shsurf->ping_timer = NULL;
-}
+handle_shell_client_destroy(struct wl_listener *listener, void *data);
static int
-ping_timeout_handler(void *data)
+xdg_ping_timeout_handler(void *data)
{
- struct shell_surface *shsurf = data;
+ struct shell_client *sc = data;
struct weston_seat *seat;
+ struct shell_surface *shsurf;
/* Client is not responding */
- shsurf->unresponsive = 1;
-
- wl_list_for_each(seat, &shsurf->surface->compositor->seat_list, link)
- if (seat->pointer && seat->pointer->focus &&
- seat->pointer->focus->surface == shsurf->surface)
+ sc->unresponsive = 1;
+ wl_list_for_each(seat, &sc->shell->compositor->seat_list, link) {
+ if (seat->pointer == NULL || seat->pointer->focus == NULL)
+ continue;
+ if (seat->pointer->focus->surface->resource == NULL)
+ continue;
+
+ shsurf = get_shell_surface(seat->pointer->focus->surface);
+ if (shsurf &&
+ wl_resource_get_client(shsurf->resource) == sc->client)
set_busy_cursor(shsurf, seat->pointer);
+ }
return 1;
}
static void
+handle_xdg_ping(struct shell_surface *shsurf, uint32_t serial)
+{
+ struct weston_compositor *compositor = shsurf->shell->compositor;
+ struct wl_client *client = wl_resource_get_client(shsurf->resource);
+ struct shell_client *sc;
+ struct wl_event_loop *loop;
+ static const int ping_timeout = 200;
+
+ sc = get_shell_client(client);
+ if (sc->unresponsive) {
+ xdg_ping_timeout_handler(sc);
+ return;
+ }
+
+ sc->ping_serial = serial;
+ loop = wl_display_get_event_loop(compositor->wl_display);
+ if (sc->ping_timer == NULL)
+ sc->ping_timer =
+ wl_event_loop_add_timer(loop,
+ xdg_ping_timeout_handler, sc);
+ if (sc->ping_timer == NULL)
+ return;
+
+ wl_event_source_timer_update(sc->ping_timer, ping_timeout);
+
+ if (shell_surface_is_xdg_surface(shsurf) ||
+ shell_surface_is_xdg_popup(shsurf))
+ xdg_shell_send_ping(sc->resource, serial);
+ else if (shell_surface_is_wl_shell_surface(shsurf))
+ wl_shell_surface_send_ping(shsurf->resource, serial);
+}
+
+static void
ping_handler(struct weston_surface *surface, uint32_t serial)
{
struct shell_surface *shsurf = get_shell_surface(surface);
- struct wl_event_loop *loop;
- int ping_timeout = 200;
if (!shsurf)
return;
if (!shsurf->resource)
return;
-
if (shsurf->surface == shsurf->shell->grab_surface)
return;
- if (!shsurf->ping_timer) {
- shsurf->ping_timer = malloc(sizeof *shsurf->ping_timer);
- if (!shsurf->ping_timer)
- return;
-
- shsurf->ping_timer->serial = serial;
- loop = wl_display_get_event_loop(surface->compositor->wl_display);
- shsurf->ping_timer->source =
- wl_event_loop_add_timer(loop, ping_timeout_handler, shsurf);
- wl_event_source_timer_update(shsurf->ping_timer->source, ping_timeout);
-
- if (shell_surface_is_wl_shell_surface(shsurf))
- wl_shell_surface_send_ping(shsurf->resource, serial);
- else if (shell_surface_is_xdg_surface(shsurf))
- xdg_surface_send_ping(shsurf->resource, serial);
- else if (shell_surface_is_xdg_popup(shsurf))
- xdg_popup_send_ping(shsurf->resource, serial);
- }
+ handle_xdg_ping(shsurf, serial);
}
static void
@@ -1882,21 +1909,14 @@ handle_pointer_focus(struct wl_listener *listener, void *data)
struct weston_pointer *pointer = data;
struct weston_view *view = pointer->focus;
struct weston_compositor *compositor;
- struct shell_surface *shsurf;
uint32_t serial;
if (!view)
return;
compositor = view->surface->compositor;
- shsurf = get_shell_surface(view->surface);
-
- if (shsurf && shsurf->unresponsive) {
- set_busy_cursor(shsurf, pointer);
- } else {
- serial = wl_display_next_serial(compositor->wl_display);
- ping_handler(view->surface, serial);
- }
+ serial = wl_display_next_serial(compositor->wl_display);
+ ping_handler(view->surface, serial);
}
static void
@@ -1913,49 +1933,92 @@ create_pointer_focus_listener(struct weston_seat *seat)
}
static void
-xdg_surface_set_transient_for(struct wl_client *client,
- struct wl_resource *resource,
- struct wl_resource *parent_resource)
+shell_surface_lose_keyboard_focus(struct shell_surface *shsurf)
{
- struct shell_surface *shsurf = wl_resource_get_user_data(resource);
- struct weston_surface *parent;
+ if (--shsurf->focus_count == 0)
+ if (shell_surface_is_xdg_surface(shsurf))
+ xdg_surface_send_deactivated(shsurf->resource);
+}
- if (parent_resource)
- parent = wl_resource_get_user_data(parent_resource);
- else
- parent = NULL;
+static void
+shell_surface_gain_keyboard_focus(struct shell_surface *shsurf)
+{
+ if (shsurf->focus_count++ == 0)
+ if (shell_surface_is_xdg_surface(shsurf))
+ xdg_surface_send_activated(shsurf->resource);
+}
- shell_surface_set_parent(shsurf, parent);
+static void
+handle_keyboard_focus(struct wl_listener *listener, void *data)
+{
+ struct weston_keyboard *keyboard = data;
+ struct shell_seat *seat = get_shell_seat(keyboard->seat);
+
+ if (seat->focused_surface) {
+ struct shell_surface *shsurf = get_shell_surface(seat->focused_surface);
+ if (shsurf)
+ shell_surface_lose_keyboard_focus(shsurf);
+ }
+
+ seat->focused_surface = keyboard->focus;
+
+ if (seat->focused_surface) {
+ struct shell_surface *shsurf = get_shell_surface(seat->focused_surface);
+ if (shsurf)
+ shell_surface_gain_keyboard_focus(shsurf);
+ }
}
static void
-surface_pong(struct shell_surface *shsurf, uint32_t serial)
+create_keyboard_focus_listener(struct weston_seat *seat)
{
- struct weston_compositor *ec = shsurf->surface->compositor;
- struct weston_seat *seat;
+ struct wl_listener *listener;
- if (shsurf->ping_timer == NULL)
- /* Just ignore unsolicited pong. */
+ if (!seat->keyboard)
return;
- if (shsurf->ping_timer->serial == serial) {
- shsurf->unresponsive = 0;
- wl_list_for_each(seat, &ec->seat_list, link) {
- if(seat->pointer)
- end_busy_cursor(shsurf, seat->pointer);
- }
- ping_timer_destroy(shsurf);
- }
+ listener = malloc(sizeof *listener);
+ listener->notify = handle_keyboard_focus;
+ wl_signal_add(&seat->keyboard->focus_signal, listener);
+}
+
+static struct shell_client *
+get_shell_client(struct wl_client *client)
+{
+ struct wl_listener *listener;
+
+ listener = wl_client_get_destroy_listener(client,
+ handle_shell_client_destroy);
+ if (listener == NULL)
+ return NULL;
+
+ return container_of(listener, struct shell_client, destroy_listener);
}
static void
-shell_surface_pong(struct wl_client *client, struct wl_resource *resource,
- uint32_t serial)
+shell_client_pong(struct shell_client *sc, uint32_t serial)
{
- struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ if (sc->ping_serial != serial)
+ return;
+
+ sc->unresponsive = 0;
+ end_busy_cursor(sc->shell->compositor, sc->client);
- surface_pong(shsurf, serial);
+ if (sc->ping_timer) {
+ wl_event_source_remove(sc->ping_timer);
+ sc->ping_timer = NULL;
+ }
+
+}
+
+static void
+shell_surface_pong(struct wl_client *client,
+ struct wl_resource *resource, uint32_t serial)
+{
+ struct shell_client *sc;
+ sc = get_shell_client(client);
+ shell_client_pong(sc, serial);
}
static void
@@ -2228,8 +2291,6 @@ set_fullscreen(struct shell_surface *shsurf,
shsurf->fullscreen.type = method;
shsurf->fullscreen.framerate = framerate;
- shsurf->next_state.fullscreen = true;
- shsurf->state_changed = true;
shsurf->type = SHELL_SURFACE_TOPLEVEL;
shsurf->client->send_configure(shsurf->surface, 0,
@@ -2298,6 +2359,9 @@ shell_surface_set_fullscreen(struct wl_client *client,
surface_clear_next_states(shsurf);
set_fullscreen(shsurf, method, framerate, output);
+
+ shsurf->next_state.fullscreen = true;
+ shsurf->state_changed = true;
}
static void
@@ -2356,8 +2420,6 @@ set_maximized(struct shell_surface *shsurf,
shsurf->output->width,
shsurf->output->height - panel_height);
- shsurf->next_state.maximized = true;
- shsurf->state_changed = true;
shsurf->type = SHELL_SURFACE_TOPLEVEL;
}
@@ -2383,6 +2445,53 @@ unset_maximized(struct shell_surface *shsurf)
}
static void
+set_minimized(struct weston_surface *surface, uint32_t is_true)
+{
+ struct shell_surface *shsurf;
+ struct workspace *current_ws;
+ struct weston_seat *seat;
+ struct weston_surface *focus;
+ struct weston_view *view;
+
+ view = get_default_view(surface);
+ if (!view)
+ return;
+
+ assert(weston_surface_get_main_surface(view->surface) == view->surface);
+
+ shsurf = get_shell_surface(surface);
+ current_ws = get_current_workspace(shsurf->shell);
+
+ wl_list_remove(&view->layer_link);
+ /* hide or show, depending on the state */
+ if (is_true) {
+ wl_list_insert(&shsurf->shell->minimized_layer.view_list, &view->layer_link);
+
+ drop_focus_state(shsurf->shell, current_ws, view->surface);
+ wl_list_for_each(seat, &shsurf->shell->compositor->seat_list, link) {
+ if (!seat->keyboard)
+ continue;
+ focus = weston_surface_get_main_surface(seat->keyboard->focus);
+ if (focus == view->surface)
+ weston_keyboard_set_focus(seat->keyboard, NULL);
+ }
+ }
+ else {
+ wl_list_insert(&current_ws->layer.view_list, &view->layer_link);
+
+ wl_list_for_each(seat, &shsurf->shell->compositor->seat_list, link) {
+ if (!seat->keyboard)
+ continue;
+ activate(shsurf->shell, view->surface, seat);
+ }
+ }
+
+ shell_surface_update_child_surface_layers(shsurf);
+
+ weston_view_damage_below(view);
+}
+
+static void
shell_surface_set_maximized(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output_resource)
@@ -2399,6 +2508,9 @@ shell_surface_set_maximized(struct wl_client *client,
surface_clear_next_states(shsurf);
set_maximized(shsurf, output);
+
+ shsurf->next_state.maximized = true;
+ shsurf->state_changed = true;
}
/* This is only ever called from set_surface_type(), so there’s no need to
@@ -2596,11 +2708,11 @@ shell_configure_fullscreen(struct shell_surface *shsurf)
case WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER:
if (shell_surface_is_top_fullscreen(shsurf)) {
struct weston_mode mode = {0,
- surf_width * surface->buffer_viewport.scale,
- surf_height * surface->buffer_viewport.scale,
+ surf_width * surface->buffer_viewport.buffer.scale,
+ surf_height * surface->buffer_viewport.buffer.scale,
shsurf->fullscreen.framerate};
- if (weston_output_switch_mode(output, &mode, surface->buffer_viewport.scale,
+ if (weston_output_switch_mode(output, &mode, surface->buffer_viewport.buffer.scale,
WESTON_MODE_SWITCH_SET_TEMPORARY) == 0) {
weston_view_set_position(shsurf->view,
output->x - surf_x,
@@ -2921,16 +3033,13 @@ destroy_shell_surface(struct shell_surface *shsurf)
*/
wl_list_remove(&shsurf->surface_destroy_listener.link);
shsurf->surface->configure = NULL;
- ping_timer_destroy(shsurf);
free(shsurf->title);
weston_view_destroy(shsurf->view);
wl_list_remove(&shsurf->children_link);
- wl_list_for_each_safe(child, next, &shsurf->children_list, children_link) {
- wl_list_remove(&child->children_link);
- child->parent = NULL;
- }
+ wl_list_for_each_safe(child, next, &shsurf->children_list, children_link)
+ shell_surface_set_parent(child, NULL);
wl_list_remove(&shsurf->link);
free(shsurf);
@@ -2941,7 +3050,9 @@ shell_destroy_shell_surface(struct wl_resource *resource)
{
struct shell_surface *shsurf = wl_resource_get_user_data(resource);
- destroy_shell_surface(shsurf);
+ if (!wl_list_empty(&shsurf->popup.grab_link))
+ remove_popup_grab(shsurf);
+ shsurf->resource = NULL;
}
static void
@@ -2958,9 +3069,33 @@ shell_handle_surface_destroy(struct wl_listener *listener, void *data)
}
static void
-shell_surface_configure(struct weston_surface *, int32_t, int32_t);
+fade_out_done(struct weston_view_animation *animation, void *data)
+{
+ struct shell_surface *shsurf = data;
+
+ weston_surface_destroy(shsurf->surface);
+}
+
static void
-shell_surface_output_destroyed(struct weston_surface *);
+handle_resource_destroy(struct wl_listener *listener, void *data)
+{
+ struct shell_surface *shsurf =
+ container_of(listener, struct shell_surface,
+ resource_destroy_listener);
+
+ shsurf->surface->ref_count++;
+
+ pixman_region32_fini(&shsurf->surface->pending.input);
+ pixman_region32_init(&shsurf->surface->pending.input);
+ pixman_region32_fini(&shsurf->surface->input);
+ pixman_region32_init(&shsurf->surface->input);
+ if (weston_surface_is_mapped(shsurf->surface))
+ weston_fade_run(shsurf->view, 1.0, 0.0, 300.0,
+ fade_out_done, shsurf);
+}
+
+static void
+shell_surface_configure(struct weston_surface *, int32_t, int32_t);
struct shell_surface *
get_shell_surface(struct weston_surface *surface)
@@ -2997,7 +3132,10 @@ create_common_surface(void *shell, struct weston_surface *surface,
surface->configure = shell_surface_configure;
surface->configure_private = shsurf;
- surface->output_destroyed = shell_surface_output_destroyed;
+
+ shsurf->resource_destroy_listener.notify = handle_resource_destroy;
+ wl_resource_add_destroy_listener(surface->resource,
+ &shsurf->resource_destroy_listener);
shsurf->shell = (struct desktop_shell *) shell;
shsurf->unresponsive = 0;
@@ -3008,7 +3146,6 @@ create_common_surface(void *shell, struct weston_surface *surface,
shsurf->fullscreen.type = WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT;
shsurf->fullscreen.framerate = 0;
shsurf->fullscreen.black_view = NULL;
- shsurf->ping_timer = NULL;
wl_list_init(&shsurf->fullscreen.transform.link);
wl_signal_init(&shsurf->destroy_signal);
@@ -3087,9 +3224,13 @@ shell_get_shell_surface(struct wl_client *client,
static bool
shell_surface_is_wl_shell_surface(struct shell_surface *shsurf)
{
- return wl_resource_instance_of(shsurf->resource,
- &wl_shell_surface_interface,
- &shell_surface_implementation);
+ /* A shell surface without a resource is created from xwayland
+ * and is considered a wl_shell surface for now. */
+
+ return shsurf->resource == NULL ||
+ wl_resource_instance_of(shsurf->resource,
+ &wl_shell_surface_interface,
+ &shell_surface_implementation);
}
static const struct wl_shell_interface shell_implementation = {
@@ -3107,13 +3248,31 @@ xdg_surface_destroy(struct wl_client *client,
}
static void
-xdg_surface_pong(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t serial)
+xdg_surface_set_transient_for(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *parent_resource)
{
struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ struct weston_surface *parent;
+
+ if (parent_resource)
+ parent = wl_resource_get_user_data(parent_resource);
+ else
+ parent = NULL;
- surface_pong(shsurf, serial);
+ shell_surface_set_parent(shsurf, parent);
+}
+
+static void
+xdg_surface_set_margin(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t left,
+ int32_t right,
+ int32_t top,
+ int32_t bottom)
+{
+ /* Do nothing, Weston doesn't try to constrain or place
+ * surfaces in any special manner... */
}
static void
@@ -3168,103 +3327,97 @@ xdg_surface_set_output(struct wl_client *client,
}
static void
-xdg_surface_set_fullscreen(struct wl_client *client,
- struct wl_resource *resource)
+xdg_surface_change_state(struct shell_surface *shsurf,
+ uint32_t state, uint32_t value, uint32_t serial)
{
- struct shell_surface *shsurf = wl_resource_get_user_data(resource);
+ xdg_surface_send_change_state(shsurf->resource, state, value, serial);
+ shsurf->state_requested = true;
- if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
- return;
+ switch (state) {
+ case XDG_SURFACE_STATE_MAXIMIZED:
+ shsurf->requested_state.maximized = value;
+ if (value)
+ set_maximized(shsurf, NULL);
+ break;
+ case XDG_SURFACE_STATE_FULLSCREEN:
+ shsurf->requested_state.fullscreen = value;
- if (!shsurf->next_state.fullscreen)
- set_fullscreen(shsurf,
- WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
- 0, shsurf->recommended_output);
+ if (value)
+ set_fullscreen(shsurf,
+ WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
+ 0, shsurf->recommended_output);
+ break;
+ }
}
static void
-xdg_surface_unset_fullscreen(struct wl_client *client,
- struct wl_resource *resource)
+xdg_surface_request_change_state(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t state,
+ uint32_t value,
+ uint32_t serial)
{
struct shell_surface *shsurf = wl_resource_get_user_data(resource);
- int32_t width, height;
+
+ /* The client can't know what the current state is, so we need
+ to always send a state change in response. */
if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
return;
- if (!shsurf->next_state.fullscreen)
+ switch (state) {
+ case XDG_SURFACE_STATE_MAXIMIZED:
+ case XDG_SURFACE_STATE_FULLSCREEN:
+ break;
+ default:
+ /* send error? ignore? send change state with value 0? */
return;
-
- shsurf->next_state.fullscreen = false;
- shsurf->state_changed = true;
-
- if (shsurf->saved_size_valid) {
- width = shsurf->saved_width;
- height = shsurf->saved_height;
- shsurf->saved_size_valid = false;
- } else {
- width = shsurf->surface->width;
- height = shsurf->surface->height;
}
- shsurf->client->send_configure(shsurf->surface, 0, width, height);
+ xdg_surface_change_state(shsurf, state, value, serial);
}
static void
-xdg_surface_set_maximized(struct wl_client *client,
- struct wl_resource *resource)
+xdg_surface_ack_change_state(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t state,
+ uint32_t value,
+ uint32_t serial)
{
struct shell_surface *shsurf = wl_resource_get_user_data(resource);
- if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
- return;
-
- if (!shsurf->next_state.maximized)
- set_maximized(shsurf, NULL);
+ if (shsurf->state_requested) {
+ shsurf->next_state = shsurf->requested_state;
+ shsurf->state_changed = true;
+ shsurf->state_requested = false;
+ }
}
static void
-xdg_surface_unset_maximized(struct wl_client *client,
+xdg_surface_set_minimized(struct wl_client *client,
struct wl_resource *resource)
{
struct shell_surface *shsurf = wl_resource_get_user_data(resource);
- int32_t width, height;
if (shsurf->type != SHELL_SURFACE_TOPLEVEL)
return;
- if (!shsurf->next_state.maximized)
- return;
-
- shsurf->next_state.maximized = false;
- shsurf->state_changed = true;
-
- if (shsurf->saved_size_valid) {
- width = shsurf->saved_width;
- height = shsurf->saved_height;
- shsurf->saved_size_valid = false;
- } else {
- width = shsurf->surface->width;
- height = shsurf->surface->height;
- }
-
- shsurf->client->send_configure(shsurf->surface, 0, width, height);
+ /* apply compositor's own minimization logic (hide) */
+ set_minimized(shsurf->surface, 1);
}
static const struct xdg_surface_interface xdg_surface_implementation = {
xdg_surface_destroy,
xdg_surface_set_transient_for,
+ xdg_surface_set_margin,
xdg_surface_set_title,
xdg_surface_set_app_id,
- xdg_surface_pong,
xdg_surface_move,
xdg_surface_resize,
xdg_surface_set_output,
- xdg_surface_set_fullscreen,
- xdg_surface_unset_fullscreen,
- xdg_surface_set_maximized,
- xdg_surface_unset_maximized,
- NULL /* set_minimized */
+ xdg_surface_request_change_state,
+ xdg_surface_ack_change_state,
+ xdg_surface_set_minimized
};
static void
@@ -3275,7 +3428,7 @@ xdg_send_configure(struct weston_surface *surface,
assert(shsurf);
- xdg_surface_send_configure(shsurf->resource, edges, width, height);
+ xdg_surface_send_configure(shsurf->resource, width, height);
}
static const struct weston_shell_client xdg_client = {
@@ -3315,13 +3468,14 @@ xdg_get_xdg_surface(struct wl_client *client,
{
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
- struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct shell_client *sc = wl_resource_get_user_data(resource);
+ struct desktop_shell *shell = sc->shell;
struct shell_surface *shsurf;
if (get_shell_surface(surface)) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
- "desktop_shell::get_shell_surface already requested");
+ "xdg_shell::get_xdg_surface already requested");
return;
}
@@ -3344,9 +3498,10 @@ xdg_get_xdg_surface(struct wl_client *client,
static bool
shell_surface_is_xdg_surface(struct shell_surface *shsurf)
{
- return wl_resource_instance_of(shsurf->resource,
- &xdg_surface_interface,
- &xdg_surface_implementation);
+ return shsurf->resource &&
+ wl_resource_instance_of(shsurf->resource,
+ &xdg_surface_interface,
+ &xdg_surface_implementation);
}
/* xdg-popup implementation */
@@ -3358,19 +3513,8 @@ xdg_popup_destroy(struct wl_client *client,
wl_resource_destroy(resource);
}
-static void
-xdg_popup_pong(struct wl_client *client,
- struct wl_resource *resource,
- uint32_t serial)
-{
- struct shell_surface *shsurf = wl_resource_get_user_data(resource);
-
- surface_pong(shsurf, serial);
-}
-
static const struct xdg_popup_interface xdg_popup_implementation = {
xdg_popup_destroy,
- xdg_popup_pong
};
static void
@@ -3416,7 +3560,8 @@ xdg_get_xdg_popup(struct wl_client *client,
{
struct weston_surface *surface =
wl_resource_get_user_data(surface_resource);
- struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct shell_client *sc = wl_resource_get_user_data(resource);
+ struct desktop_shell *shell = sc->shell;
struct shell_surface *shsurf;
struct weston_surface *parent;
struct shell_seat *seat;
@@ -3424,7 +3569,7 @@ xdg_get_xdg_popup(struct wl_client *client,
if (get_shell_surface(surface)) {
wl_resource_post_error(surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
- "desktop_shell::get_shell_surface already requested");
+ "xdg_shell::get_xdg_popup already requested");
return;
}
@@ -3454,6 +3599,15 @@ xdg_get_xdg_popup(struct wl_client *client,
shsurf, shell_destroy_shell_surface);
}
+static void
+xdg_pong(struct wl_client *client,
+ struct wl_resource *resource, uint32_t serial)
+{
+ struct shell_client *sc = wl_resource_get_user_data(resource);
+
+ shell_client_pong(sc, serial);
+}
+
static bool
shell_surface_is_xdg_popup(struct shell_surface *shsurf)
{
@@ -3465,7 +3619,8 @@ shell_surface_is_xdg_popup(struct shell_surface *shsurf)
static const struct xdg_shell_interface xdg_implementation = {
xdg_use_unstable_version,
xdg_get_xdg_surface,
- xdg_get_xdg_popup
+ xdg_get_xdg_popup,
+ xdg_pong
};
static int
@@ -3475,7 +3630,7 @@ xdg_shell_unversioned_dispatch(const void *implementation,
union wl_argument *args)
{
struct wl_resource *resource = _target;
- struct desktop_shell *shell = wl_resource_get_user_data(resource);
+ struct shell_client *sc = wl_resource_get_user_data(resource);
if (opcode != 0) {
wl_resource_post_error(resource,
@@ -3484,7 +3639,7 @@ xdg_shell_unversioned_dispatch(const void *implementation,
return 0;
}
-#define XDG_SERVER_VERSION 1
+#define XDG_SERVER_VERSION 3
static_assert(XDG_SERVER_VERSION == XDG_SHELL_VERSION_CURRENT,
"shell implementation doesn't match protocol version");
@@ -3499,7 +3654,7 @@ xdg_shell_unversioned_dispatch(const void *implementation,
}
wl_resource_set_implementation(resource, &xdg_implementation,
- shell, NULL);
+ sc, NULL);
return 1;
}
@@ -3733,18 +3888,19 @@ resume_desktop(struct desktop_shell *shell)
terminate_screensaver(shell);
wl_list_remove(&shell->lock_layer.link);
- wl_list_insert(&shell->compositor->cursor_layer.link,
- &shell->fullscreen_layer.link);
- wl_list_insert(&shell->fullscreen_layer.link,
- &shell->panel_layer.link);
if (shell->showing_input_panels) {
- wl_list_insert(&shell->panel_layer.link,
+ wl_list_insert(&shell->compositor->cursor_layer.link,
&shell->input_panel_layer.link);
wl_list_insert(&shell->input_panel_layer.link,
- &ws->layer.link);
+ &shell->fullscreen_layer.link);
} else {
- wl_list_insert(&shell->panel_layer.link, &ws->layer.link);
+ wl_list_insert(&shell->compositor->cursor_layer.link,
+ &shell->fullscreen_layer.link);
}
+ wl_list_insert(&shell->fullscreen_layer.link,
+ &shell->panel_layer.link);
+ wl_list_insert(&shell->panel_layer.link,
+ &ws->layer.link),
restore_focus_state(shell, get_current_workspace(shell));
@@ -3832,9 +3988,10 @@ move_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *dat
static void
maximize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data)
{
- struct weston_surface *focus = seat->pointer->focus->surface;
+ struct weston_surface *focus = seat->keyboard->focus;
struct weston_surface *surface;
struct shell_surface *shsurf;
+ uint32_t serial;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
@@ -3847,18 +4004,18 @@ maximize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void
if (!shell_surface_is_xdg_surface(shsurf))
return;
- if (shsurf->state.maximized)
- xdg_surface_send_request_unset_maximized(shsurf->resource);
- else
- xdg_surface_send_request_set_maximized(shsurf->resource);
+ serial = wl_display_next_serial(seat->compositor->wl_display);
+ xdg_surface_change_state(shsurf, XDG_SURFACE_STATE_MAXIMIZED,
+ !shsurf->state.maximized, serial);
}
static void
fullscreen_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void *data)
{
- struct weston_surface *focus = seat->pointer->focus->surface;
+ struct weston_surface *focus = seat->keyboard->focus;
struct weston_surface *surface;
struct shell_surface *shsurf;
+ uint32_t serial;
surface = weston_surface_get_main_surface(focus);
if (surface == NULL)
@@ -3871,10 +4028,9 @@ fullscreen_binding(struct weston_seat *seat, uint32_t time, uint32_t button, voi
if (!shell_surface_is_xdg_surface(shsurf))
return;
- if (shsurf->state.fullscreen)
- xdg_surface_send_request_unset_fullscreen(shsurf->resource);
- else
- xdg_surface_send_request_set_fullscreen(shsurf->resource);
+ serial = wl_display_next_serial(seat->compositor->wl_display);
+ xdg_surface_change_state(shsurf, XDG_SURFACE_STATE_FULLSCREEN,
+ !shsurf->state.fullscreen, serial);
}
static void
@@ -4248,14 +4404,14 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
else
restore_all_output_modes(shell->compositor);
+ /* Update the surface’s layer. This brings it to the top of the stacking
+ * order as appropriate. */
+ shell_surface_update_layer(shsurf);
+
if (shell->focus_animation_type != ANIMATION_NONE) {
ws = get_current_workspace(shell);
animate_focus_change(shell, ws, get_default_view(old_es), get_default_view(es));
}
-
- /* Update the surface’s layer. This brings it to the top of the stacking
- * order as appropriate. */
- shell_surface_update_layer(shsurf);
}
/* no-op func for checking black surface */
@@ -4681,10 +4837,6 @@ map(struct desktop_shell *shell, struct shell_surface *shsurf,
}
}
- if ((shsurf->type == SHELL_SURFACE_XWAYLAND || shsurf->state.relative) &&
- shsurf->transient.flags == WL_SHELL_SURFACE_TRANSIENT_INACTIVE) {
- }
-
switch (shsurf->type) {
/* XXX: xwayland's using the same fields for transient type */
case SHELL_SURFACE_XWAYLAND:
@@ -4788,11 +4940,22 @@ shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
} else if (type_changed || sx != 0 || sy != 0 ||
shsurf->last_width != es->width ||
shsurf->last_height != es->height) {
- shsurf->last_width = es->width;
- shsurf->last_height = es->height;
float from_x, from_y;
float to_x, to_y;
+ if (shsurf->resize_edges) {
+ sx = 0;
+ sy = 0;
+ }
+
+ if (shsurf->resize_edges & WL_SHELL_SURFACE_RESIZE_LEFT)
+ sx = shsurf->last_width - es->width;
+ if (shsurf->resize_edges & WL_SHELL_SURFACE_RESIZE_TOP)
+ sy = shsurf->last_height - es->height;
+
+ shsurf->last_width = es->width;
+ shsurf->last_height = es->height;
+
weston_view_to_global_float(shsurf->view, 0, 0, &from_x, &from_y);
weston_view_to_global_float(shsurf->view, sx, sy, &to_x, &to_y);
configure(shell, es,
@@ -4801,19 +4964,6 @@ shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
}
}
-static void
-shell_surface_output_destroyed(struct weston_surface *es)
-{
- struct shell_surface *shsurf = get_shell_surface(es);
-
- assert(shsurf);
-
- shsurf->saved_position_valid = false;
- shsurf->next_state.maximized = false;
- shsurf->next_state.fullscreen = false;
- shsurf->state_changed = true;
-}
-
static void launch_desktop_shell_process(void *data);
static void
@@ -4875,14 +5025,53 @@ launch_desktop_shell_process(void *data)
}
static void
+handle_shell_client_destroy(struct wl_listener *listener, void *data)
+{
+ struct shell_client *sc =
+ container_of(listener, struct shell_client, destroy_listener);
+
+ if (sc->ping_timer)
+ wl_event_source_remove(sc->ping_timer);
+ free(sc);
+}
+
+static struct shell_client *
+shell_client_create(struct wl_client *client, struct desktop_shell *shell,
+ const struct wl_interface *interface, uint32_t id)
+{
+ struct shell_client *sc;
+
+ sc = zalloc(sizeof *sc);
+ if (sc == NULL) {
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ sc->resource = wl_resource_create(client, interface, 1, id);
+ if (sc->resource == NULL) {
+ free(sc);
+ wl_client_post_no_memory(client);
+ return NULL;
+ }
+
+ sc->client = client;
+ sc->shell = shell;
+ sc->destroy_listener.notify = handle_shell_client_destroy;
+ wl_client_add_destroy_listener(client, &sc->destroy_listener);
+
+ return sc;
+}
+
+static void
bind_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct desktop_shell *shell = data;
- struct wl_resource *resource;
+ struct shell_client *sc;
- resource = wl_resource_create(client, &wl_shell_interface, 1, id);
- if (resource)
- wl_resource_set_implementation(resource, &shell_implementation,
+ sc = shell_client_create(client, shell, &wl_shell_interface, id);
+ if (sc)
+ wl_resource_set_implementation(sc->resource,
+ &shell_implementation,
shell, NULL);
}
@@ -4890,13 +5079,13 @@ static void
bind_xdg_shell(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
struct desktop_shell *shell = data;
- struct wl_resource *resource;
+ struct shell_client *sc;
- resource = wl_resource_create(client, &xdg_shell_interface, 1, id);
- if (resource)
- wl_resource_set_dispatcher(resource,
+ sc = shell_client_create(client, shell, &xdg_shell_interface, id);
+ if (sc)
+ wl_resource_set_dispatcher(sc->resource,
xdg_shell_unversioned_dispatch,
- NULL, shell, NULL);
+ NULL, sc, NULL);
}
static void
@@ -5025,6 +5214,7 @@ struct switcher {
struct weston_surface *current;
struct wl_listener listener;
struct weston_keyboard_grab grab;
+ struct wl_array minimized_array;
};
static void
@@ -5035,6 +5225,16 @@ switcher_next(struct switcher *switcher)
struct shell_surface *shsurf;
struct workspace *ws = get_current_workspace(switcher->shell);
+ /* temporary re-display minimized surfaces */
+ struct weston_view *tmp;
+ struct weston_view **minimized;
+ wl_list_for_each_safe(view, tmp, &switcher->shell->minimized_layer.view_list, layer_link) {
+ wl_list_remove(&view->layer_link);
+ wl_list_insert(&ws->layer.view_list, &view->layer_link);
+ minimized = wl_array_add(&switcher->minimized_array, sizeof *minimized);
+ *minimized = view;
+ }
+
wl_list_for_each(view, &ws->layer.view_list, layer_link) {
shsurf = get_shell_surface(view->surface);
if (shsurf &&
@@ -5106,6 +5306,19 @@ switcher_destroy(struct switcher *switcher)
weston_keyboard_end_grab(keyboard);
if (keyboard->input_method_resource)
keyboard->grab = &keyboard->input_method_grab;
+
+ /* re-hide surfaces that were temporary shown during the switch */
+ struct weston_view **minimized;
+ wl_array_for_each(minimized, &switcher->minimized_array) {
+ /* with the exception of the current selected */
+ if ((*minimized)->surface != switcher->current) {
+ wl_list_remove(&(*minimized)->layer_link);
+ wl_list_insert(&switcher->shell->minimized_layer.view_list, &(*minimized)->layer_link);
+ weston_view_damage_below(*minimized);
+ }
+ }
+ wl_array_release(&switcher->minimized_array);
+
free(switcher);
}
@@ -5158,6 +5371,7 @@ switcher_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
switcher->current = NULL;
switcher->listener.notify = switcher_handle_surface_destroy;
wl_list_init(&switcher->listener.link);
+ wl_array_init(&switcher->minimized_array);
restore_all_output_modes(shell->compositor);
lower_fullscreen_layer(switcher->shell);
@@ -5433,11 +5647,81 @@ workspace_move_surface_down_binding(struct weston_seat *seat, uint32_t time,
}
static void
+shell_reposition_view_on_output_destroy(struct weston_view *view)
+{
+ struct weston_output *output, *first_output;
+ struct weston_compositor *ec = view->surface->compositor;
+ struct shell_surface *shsurf;
+ float x, y;
+ int visible;
+
+ x = view->geometry.x;
+ y = view->geometry.y;
+
+ /* At this point the destroyed output is not in the list anymore.
+ * If the view is still visible somewhere, we leave where it is,
+ * otherwise, move it to the first output. */
+ visible = 0;
+ wl_list_for_each(output, &ec->output_list, link) {
+ if (pixman_region32_contains_point(&output->region,
+ x, y, NULL)) {
+ visible = 1;
+ break;
+ }
+ }
+
+ if (!visible) {
+ first_output = container_of(ec->output_list.next,
+ struct weston_output, link);
+
+ x = first_output->x + first_output->width / 4;
+ y = first_output->y + first_output->height / 4;
+ }
+
+ weston_view_set_position(view, x, y);
+
+ shsurf = get_shell_surface(view->surface);
+
+ if (shsurf) {
+ shsurf->saved_position_valid = false;
+ shsurf->next_state.maximized = false;
+ shsurf->next_state.fullscreen = false;
+ shsurf->state_changed = true;
+ }
+}
+
+static void
+shell_reposition_views_on_output_destroy(struct shell_output *shell_output)
+{
+ struct desktop_shell *shell = shell_output->shell;
+ struct weston_output *output = shell_output->output;
+ struct weston_layer *layer;
+ struct weston_view *view;
+
+ /* Move all views in the layers owned by the shell */
+ wl_list_for_each(layer, shell->fullscreen_layer.link.prev, link) {
+ wl_list_for_each(view, &layer->view_list, layer_link) {
+ if (view->output != output)
+ continue;
+
+ shell_reposition_view_on_output_destroy(view);
+ }
+
+ /* We don't start from the beggining of the layer list, so
+ * make sure we don't wrap around it. */
+ if (layer == &shell->background_layer)
+ break;
+ }
+}
+
+static void
handle_output_destroy(struct wl_listener *listener, void *data)
{
struct shell_output *output_listener =
container_of(listener, struct shell_output, destroy_listener);
+ shell_reposition_views_on_output_destroy(output_listener);
+
wl_list_remove(&output_listener->destroy_listener.link);
wl_list_remove(&output_listener->link);
free(output_listener);
@@ -5472,6 +5756,37 @@ handle_output_create(struct wl_listener *listener, void *data)
}
static void
+handle_output_move(struct wl_listener *listener, void *data)
+{
+ struct desktop_shell *shell;
+ struct weston_output *output;
+ struct weston_layer *layer;
+ struct weston_view *view;
+ float x, y;
+
+ shell = container_of(listener, struct desktop_shell,
+ output_move_listener);
+ output = data;
+
+ /* Move all views in the layers owned by the shell */
+ wl_list_for_each(layer, shell->fullscreen_layer.link.prev, link) {
+ wl_list_for_each(view, &layer->view_list, layer_link) {
+ if (view->output != output)
+ continue;
+
+ x = view->geometry.x + output->move_x;
+ y = view->geometry.y + output->move_y;
+ weston_view_set_position(view, x, y);
+ }
+
+ /* We don't start from the beggining of the layer list, so
+ * make sure we don't wrap around it. */
+ if (layer == &shell->background_layer)
+ break;
+ }
+}
+
+static void
setup_output_destroy_handler(struct weston_compositor *ec,
struct desktop_shell *shell)
{
@@ -5484,6 +5799,9 @@ setup_output_destroy_handler(struct weston_compositor *ec,
shell->output_create_listener.notify = handle_output_create;
wl_signal_add(&ec->output_created_signal,
&shell->output_create_listener);
+
+ shell->output_move_listener.notify = handle_output_move;
+ wl_signal_add(&ec->output_moved_signal, &shell->output_move_listener);
}
static void
@@ -5534,6 +5852,9 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
weston_compositor_add_button_binding(ec, BTN_LEFT, 0,
click_to_activate_binding,
shell);
+ weston_compositor_add_button_binding(ec, BTN_RIGHT, 0,
+ click_to_activate_binding,
+ shell);
weston_compositor_add_touch_binding(ec, 0,
touch_to_activate_binding,
shell);
@@ -5633,7 +5954,6 @@ module_init(struct weston_compositor *ec,
shell->wake_listener.notify = wake_handler;
wl_signal_add(&ec->wake_signal, &shell->wake_listener);
- ec->ping_handler = ping_handler;
ec->shell_interface.shell = shell;
ec->shell_interface.create_shell_surface = create_shell_surface;
ec->shell_interface.get_primary_view = get_primary_view;
@@ -5673,6 +5993,8 @@ module_init(struct weston_compositor *ec,
}
activate_workspace(shell, 0);
+ weston_layer_init(&shell->minimized_layer, NULL);
+
wl_list_init(&shell->workspaces.anim_sticky_list);
wl_list_init(&shell->workspaces.animation.link);
shell->workspaces.animation.frame = animate_workspace_change_frame;
@@ -5708,8 +6030,12 @@ module_init(struct weston_compositor *ec,
shell->screensaver.timer =
wl_event_loop_add_timer(loop, screensaver_timeout, shell);
- wl_list_for_each(seat, &ec->seat_list, link)
+ wl_list_for_each(seat, &ec->seat_list, link) {
create_pointer_focus_listener(seat);
+ create_keyboard_focus_listener(seat);
+ }
+
+ screenshooter_create(ec);
shell_add_bindings(ec, shell);