diff --git a/protocol/xdg-shell.xml b/protocol/xdg-shell.xml index 4a1d08aba..a2913c4c0 100644 --- a/protocol/xdg-shell.xml +++ b/protocol/xdg-shell.xml @@ -40,19 +40,22 @@ - Use this enum to check the protocol version, and it will be updated - automatically. + The 'current' member of this enum gives the version of the + protocol. Implementations can compare this to the version + they implement using static_assert to ensure the protocol and + implementation versions match. - + - Use this request in order to enable use of this interface. - - Understand and agree that one is using an unstable interface, - that will likely change in the future, breaking the API. + Negotiate the unstable version of the interface. This + mechanism is in place to ensure client and server agree on the + unstable versions of the protocol that they speak or exit + cleanly if they don't agree. This request will go away once + the xdg-shell protocol is stable. @@ -275,113 +278,87 @@ - - - Event sent from the compositor to the client requesting that the client - goes to a fullscreen state. It's the client job to call set_fullscreen - and really trigger the fullscreen state. + + + The different state values used on the surface. This is designed for + state values like maximized, fullscreen. It is paired with the + request_change_state event to ensure that both the client and the + compositor setting the state can be synchronized. + + States set in this way are double-buffered. They will get applied on + the next commit. + + Desktop environments may extend this enum by taking up a range of + values and documenting the range they chose in this description. + They are not required to document the values for the range that they + chose. Ideally, any good extensions from a desktop environment should + make its way into standardization into this enum. + + The current reserved ranges are: + + 0x0000 - 0x0FFF: xdg-shell core values, documented below. + 0x1000 - 0x1FFF: GNOME - + + A non-zero value indicates the surface is maximized. Otherwise, + the surface is unmaximized. + + + A non-zero value indicates the surface is fullscreen. Otherwise, + the surface is not fullscreen. + + - - - Event sent from the compositor to the client requesting that the client - leaves the fullscreen state. It's the client job to call - unset_fullscreen and really leave the fullscreen state. - - - - - - Set the surface as fullscreen. - - After this request, the compositor should send a configure event - informing the output size. - - This request informs the compositor that the next attached buffer - committed will be in a fullscreen state. The buffer size should be the - same size as the size informed in the configure event, if the client - doesn't want to leave any empty area. - - In other words: the next attached buffer after set_maximized is the new - maximized buffer. And the surface will be positioned at the maximized - position on commit. - - A simple way to synchronize and wait for the correct configure event is - to use a wl_display.sync request right after the set_fullscreen - request. When the sync callback returns, the last configure event - received just before it will be the correct one, and should contain the - right size for the surface to maximize. - - Setting one state won't unset another state. Use - xdg_surface.unset_fullscreen for unsetting it. + + + This asks the compositor to change the state. If the compositor wants + to change the state, it will send a change_state event with the same + state_type, value, and serial, and the event flow continues as if it + it was initiated by the compositor. + + If the compositor does not want to change the state, it will send a + change_state to the client with the old value of the state. + + + + This serial is so the client can know which change_state event corresponds + to which request_change_state request it sent out. + - - - Unset the surface fullscreen state. - - Same negotiation as set_fullscreen must be used. + + + This event tells the client to change a surface's state. The client + should respond with an ack_change_state request to the compositor to + guarantee that the compositor knows that the client has seen it. - - - - Event sent from the compositor to the client requesting that the client - goes to a maximized state. It's the client job to call set_maximized - and really trigger the maximized state. - + + + - - - Event sent from the compositor to the client requesting that the client - leaves the maximized state. It's the client job to call unset_maximized - and really leave the maximized state. - - - - - - Set the surface as maximized. - - After this request, the compositor will send a configure event - informing the output size minus panel and other MW decorations. - - This request informs the compositor that the next attached buffer - committed will be in a maximized state. The buffer size should be the - same size as the size informed in the configure event, if the client - doesn't want to leave any empty area. - - In other words: the next attached buffer after set_maximized is the new - maximized buffer. And the surface will be positioned at the maximized - position on commit. - - A simple way to synchronize and wait for the correct configure event is - to use a wl_display.sync request right after the set_maximized request. - When the sync callback returns, the last configure event received just - before it will be the correct one, and should contain the right size - for the surface to maximize. - - Setting one state won't unset another state. Use - xdg_surface.unset_maximized for unsetting it. - - - - - - Unset the surface maximized state. - - Same negotiation as set_maximized must be used. + + + When a change_state event is received, a client should then ack it + using the ack_change_state request to ensure that the compositor + knows the client has seen the event. + + By this point, the state is confirmed, and the next attach should + contain the buffer drawn for the new state value. + + The values here need to be the same as the values in the cooresponding + change_state event. + + + - - Set the surface minimized state. - - Setting one state won't unset another state. + + Minimize the surface. diff --git a/src/core/window.c b/src/core/window.c index 01cdc6f79..bbce5a81d 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -3120,6 +3120,9 @@ meta_window_maximize_internal (MetaWindow *window, meta_window_recalc_features (window); set_net_wm_state (window); + if (window->surface && window->maximized_horizontally && window->maximized_vertically) + meta_wayland_surface_send_maximized (window->surface); + g_object_freeze_notify (G_OBJECT (window)); g_object_notify (G_OBJECT (window), "maximized-horizontally"); g_object_notify (G_OBJECT (window), "maximized-vertically"); @@ -3605,10 +3608,13 @@ meta_window_unmaximize_internal (MetaWindow *window, set_net_wm_state (window); } - g_object_freeze_notify (G_OBJECT (window)); - g_object_notify (G_OBJECT (window), "maximized-horizontally"); - g_object_notify (G_OBJECT (window), "maximized-vertically"); - g_object_thaw_notify (G_OBJECT (window)); + if (window->surface && !window->maximized_horizontally && !window->maximized_vertically) + meta_wayland_surface_send_unmaximized (window->surface); + + g_object_freeze_notify (G_OBJECT (window)); + g_object_notify (G_OBJECT (window), "maximized-horizontally"); + g_object_notify (G_OBJECT (window), "maximized-vertically"); + g_object_thaw_notify (G_OBJECT (window)); } void @@ -3711,6 +3717,9 @@ meta_window_make_fullscreen_internal (MetaWindow *window) /* For the auto-minimize feature, if we fail to get focus */ meta_screen_queue_check_fullscreen (window->screen); + if (window->surface) + meta_wayland_surface_send_fullscreened (window->surface); + g_object_notify (G_OBJECT (window), "fullscreen"); } } @@ -3767,6 +3776,9 @@ meta_window_unmake_fullscreen (MetaWindow *window) meta_window_update_layer (window); + if (window->surface) + meta_wayland_surface_send_unfullscreened (window->surface); + g_object_notify (G_OBJECT (window), "fullscreen"); } } diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c index 14010a41e..71017cab5 100644 --- a/src/wayland/meta-wayland-surface.c +++ b/src/wayland/meta-wayland-surface.c @@ -336,22 +336,6 @@ toplevel_surface_commit (MetaWaylandSurface *surface, if (pending->frame_extents_changed) meta_window_set_custom_frame_extents (surface->window, &pending->frame_extents); - - if (pending->maximized.changed) - { - if (pending->maximized.value) - meta_window_maximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); - else - meta_window_unmaximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); - } - - if (pending->fullscreen.changed) - { - if (pending->fullscreen.value) - meta_window_make_fullscreen (surface->window); - else - meta_window_unmake_fullscreen (surface->window); - } } static void @@ -377,8 +361,6 @@ double_buffered_state_init (MetaWaylandDoubleBufferedState *state) wl_list_init (&state->frame_callback_list); state->frame_extents_changed = FALSE; - state->maximized.changed = FALSE; - state->fullscreen.changed = FALSE; } static void @@ -861,43 +843,44 @@ xdg_surface_set_output (struct wl_client *client, } static void -xdg_surface_set_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_type, + uint32_t value, + uint32_t serial) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - surface->pending.fullscreen.changed = TRUE; - surface->pending.fullscreen.value = TRUE; + surface->state_changed_serial = serial; + + switch (state_type) + { + case XDG_SURFACE_STATE_MAXIMIZED: + if (value) + meta_window_maximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); + else + meta_window_unmaximize (surface->window, META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); + break; + case XDG_SURFACE_STATE_FULLSCREEN: + if (value) + meta_window_make_fullscreen (surface->window); + else + meta_window_unmake_fullscreen (surface->window); + } } static void -xdg_surface_unset_fullscreen (struct wl_client *client, - struct wl_resource *resource) +xdg_surface_ack_change_state (struct wl_client *client, + struct wl_resource *resource, + uint32_t state_type, + uint32_t value, + uint32_t serial) { - MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - - surface->pending.fullscreen.changed = TRUE; - surface->pending.fullscreen.value = FALSE; -} - -static void -xdg_surface_set_maximized (struct wl_client *client, - struct wl_resource *resource) -{ - MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - - surface->pending.maximized.changed = TRUE; - surface->pending.maximized.value = TRUE; -} - -static void -xdg_surface_unset_maximized (struct wl_client *client, - struct wl_resource *resource) -{ - MetaWaylandSurface *surface = wl_resource_get_user_data (resource); - - surface->pending.maximized.changed = TRUE; - surface->pending.maximized.value = FALSE; + /* Do nothing for now. In the future, we'd imagine that + * we'd ignore attaches when we have a state pending that + * we haven't had the client ACK'd, to prevent a race + * condition when we have an in-flight attach when the + * client gets the new state. */ } static void @@ -918,10 +901,8 @@ static const struct xdg_surface_interface meta_wayland_xdg_surface_interface = { 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, + xdg_surface_request_change_state, + xdg_surface_ack_change_state, xdg_surface_set_minimized, }; @@ -1735,6 +1716,55 @@ meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, 0, new_width, new_height); } +static void +send_change_state (MetaWaylandSurface *surface, + uint32_t state_type, + uint32_t value) +{ + if (surface->xdg_surface.resource) + { + uint32_t serial; + + if (surface->state_changed_serial != 0) + { + serial = surface->state_changed_serial; + surface->state_changed_serial = 0; + } + else + { + struct wl_client *client = wl_resource_get_client (surface->xdg_surface.resource); + struct wl_display *display = wl_client_get_display (client); + serial = wl_display_next_serial (display); + } + + xdg_surface_send_change_state (surface->xdg_surface.resource, state_type, value, serial); + } +} + +void +meta_wayland_surface_send_maximized (MetaWaylandSurface *surface) +{ + send_change_state (surface, XDG_SURFACE_STATE_MAXIMIZED, TRUE); +} + +void +meta_wayland_surface_send_unmaximized (MetaWaylandSurface *surface) +{ + send_change_state (surface, XDG_SURFACE_STATE_MAXIMIZED, FALSE); +} + +void +meta_wayland_surface_send_fullscreened (MetaWaylandSurface *surface) +{ + send_change_state (surface, XDG_SURFACE_STATE_FULLSCREEN, TRUE); +} + +void +meta_wayland_surface_send_unfullscreened (MetaWaylandSurface *surface) +{ + send_change_state (surface, XDG_SURFACE_STATE_FULLSCREEN, FALSE); +} + void meta_wayland_surface_activated (MetaWaylandSurface *surface) { diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h index ef9ccaa4f..e0886536e 100644 --- a/src/wayland/meta-wayland-surface.h +++ b/src/wayland/meta-wayland-surface.h @@ -41,12 +41,6 @@ struct _MetaWaylandBuffer uint32_t ref_count; }; -typedef struct -{ - guint changed : 1; - guint value : 1; -} MetaWaylandStateFlag; - typedef struct { /* wl_surface.attach */ @@ -67,9 +61,6 @@ typedef struct gboolean frame_extents_changed; GtkBorder frame_extents; - - MetaWaylandStateFlag fullscreen; - MetaWaylandStateFlag maximized; } MetaWaylandDoubleBufferedState; typedef struct @@ -107,6 +98,8 @@ struct _MetaWaylandSurface GSList *pending_placement_ops; } sub; + uint32_t state_changed_serial; + /* All the pending state, that wl_surface.commit will apply. */ MetaWaylandDoubleBufferedState pending; }; @@ -124,6 +117,10 @@ void meta_wayland_surface_window_unmanaged (MetaWaylandSurface *s void meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, int width, int height); +void meta_wayland_surface_send_maximized (MetaWaylandSurface *surface); +void meta_wayland_surface_send_unmaximized (MetaWaylandSurface *surface); +void meta_wayland_surface_send_fullscreened (MetaWaylandSurface *surface); +void meta_wayland_surface_send_unfullscreened (MetaWaylandSurface *surface); void meta_wayland_surface_activated (MetaWaylandSurface *surface); void meta_wayland_surface_deactivated (MetaWaylandSurface *surface);