From 8c6aa45f97149782914d8002e0f717251b0387d2 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Sat, 8 Feb 2014 18:29:49 -0500 Subject: [PATCH] xdg-shell: Replace the set_* atrocity with a new approach Rather than require that the client implement two methods for every state, simply have one global request, change_state, and one global event, request_change_state. --- clients/simple-egl.c | 86 ++++++----------------- clients/simple-shm.c | 25 ++----- clients/window.c | 101 ++++++++------------------ desktop-shell/shell.c | 141 ++++++++++++++++++++----------------- protocol/xdg-shell.xml | 156 +++++++++++++++++------------------------ 5 files changed, 198 insertions(+), 311 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index b00b2cc5..675662f0 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -95,7 +95,7 @@ struct window { struct xdg_surface *xdg_surface; EGLSurface egl_surface; struct wl_callback *callback; - int fullscreen, configured, opaque, buffer_size, frame_sync; + int fullscreen, opaque, buffer_size, frame_sync; }; static const char *vert_shader_text = @@ -281,23 +281,25 @@ handle_surface_configure(void *data, struct xdg_surface *surface, } static void -handle_surface_request_set_maximized(void *data, struct xdg_surface *xdg_surface) +handle_surface_change_state(void *data, struct xdg_surface *xdg_surface, + uint32_t state, + uint32_t value, + uint32_t serial) { -} + struct window *window = data; -static void -handle_surface_request_unset_maximized(void *data, struct xdg_surface *xdg_surface) -{ -} + switch (state) { + case XDG_SURFACE_STATE_FULLSCREEN: + window->fullscreen = value; -static void -handle_surface_request_set_fullscreen(void *data, struct xdg_surface *xdg_surface) -{ -} + if (!value) + handle_surface_configure(window, window->xdg_surface, + window->window_size.width, + window->window_size.height); + break; + } -static void -handle_surface_request_unset_fullscreen(void *data, struct xdg_surface *xdg_surface) -{ + xdg_surface_ack_change_state(xdg_surface, state, value, serial); } static void @@ -318,53 +320,12 @@ handle_surface_delete(void *data, struct xdg_surface *xdg_surface) static const struct xdg_surface_listener xdg_surface_listener = { handle_surface_configure, - handle_surface_request_set_maximized, - handle_surface_request_unset_maximized, - handle_surface_request_set_fullscreen, - handle_surface_request_unset_fullscreen, + handle_surface_change_state, handle_surface_activated, handle_surface_deactivated, handle_surface_delete, }; -static void -configure_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct window *window = data; - - wl_callback_destroy(callback); - - window->configured = 1; -} - -static struct wl_callback_listener configure_callback_listener = { - configure_callback, -}; - -static void -set_fullscreen(struct window *window, int fullscreen) -{ - struct wl_callback *callback; - - window->fullscreen = fullscreen; - window->configured = 0; - - if (fullscreen) { - xdg_surface_set_fullscreen(window->xdg_surface); - callback = wl_display_sync(window->display->display); - wl_callback_add_listener(callback, - &configure_callback_listener, - window); - - } else { - xdg_surface_unset_fullscreen(window->xdg_surface); - handle_surface_configure(window, window->xdg_surface, - window->window_size.width, - window->window_size.height); - window->configured = 1; - } -} - static void create_surface(struct window *window) { @@ -396,7 +357,9 @@ create_surface(struct window *window) if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); - set_fullscreen(window, window->fullscreen); + xdg_surface_request_change_state(window->xdg_surface, + XDG_SURFACE_STATE_FULLSCREEN, + window->fullscreen, 0); } static void @@ -453,9 +416,6 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) if (callback) wl_callback_destroy(callback); - if (!window->configured) - return; - gettimeofday(&tv, NULL); time = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (window->frames == 0) @@ -657,7 +617,9 @@ keyboard_handle_key(void *data, struct wl_keyboard *keyboard, struct display *d = data; if (key == KEY_F11 && state) - set_fullscreen(d->window, d->window->fullscreen ^ 1); + xdg_surface_request_change_state(d->window->xdg_surface, + XDG_SURFACE_STATE_FULLSCREEN, + !d->window->fullscreen, 0); else if (key == KEY_ESC && state) running = 0; } @@ -845,8 +807,6 @@ main(int argc, char **argv) * queued up as a side effect. */ while (running && ret != -1) { wl_display_dispatch_pending(display.display); - while (!window.configured) - wl_display_dispatch(display.display); redraw(&window, NULL, 0); } diff --git a/clients/simple-shm.c b/clients/simple-shm.c index db2da5df..f84d08ce 100644 --- a/clients/simple-shm.c +++ b/clients/simple-shm.c @@ -120,22 +120,10 @@ handle_configure(void *data, struct xdg_surface *surface, } static void -handle_request_set_maximized(void *data, struct xdg_surface *xdg_surface) -{ -} - -static void -handle_request_unset_maximized(void *data, struct xdg_surface *xdg_surface) -{ -} - -static void -handle_request_set_fullscreen(void *data, struct xdg_surface *xdg_surface) -{ -} - -static void -handle_request_unset_fullscreen(void *data, struct xdg_surface *xdg_surface) +handle_change_state(void *data, struct xdg_surface *xdg_surface, + uint32_t state, + uint32_t value, + uint32_t serial) { } @@ -157,10 +145,7 @@ handle_delete(void *data, struct xdg_surface *xdg_surface) static const struct xdg_surface_listener xdg_surface_listener = { handle_configure, - handle_request_set_maximized, - handle_request_unset_maximized, - handle_request_set_fullscreen, - handle_request_unset_fullscreen, + handle_change_state, handle_activated, handle_deactivated, handle_delete, diff --git a/clients/window.c b/clients/window.c index 4dd6b36f..e5fa5411 100644 --- a/clients/window.c +++ b/clients/window.c @@ -3750,6 +3750,9 @@ window_do_resize(struct window *window) surface_set_synchronized(surface); surface_resize(surface); } + + if (!window->fullscreen && !window->maximized) + window->saved_allocation = window->pending_allocation; } static void @@ -3839,31 +3842,29 @@ handle_surface_configure(void *data, struct xdg_surface *xdg_surface, } static void -handle_surface_request_set_maximized(void *data, struct xdg_surface *xdg_surface) +handle_surface_change_state(void *data, struct xdg_surface *xdg_surface, + uint32_t state, + uint32_t value, + uint32_t serial) { struct window *window = data; - window_set_maximized(window, 1); -} -static void -handle_surface_request_unset_maximized(void *data, struct xdg_surface *xdg_surface) -{ - struct window *window = data; - window_set_maximized(window, 0); -} + switch (state) { + case XDG_SURFACE_STATE_MAXIMIZED: + window->maximized = value; + break; + case XDG_SURFACE_STATE_FULLSCREEN: + window->fullscreen = value; + break; + } -static void -handle_surface_request_set_fullscreen(void *data, struct xdg_surface *xdg_surface) -{ - struct window *window = data; - window_set_fullscreen(window, 1); -} + if (!window->fullscreen && !window->maximized) + window_schedule_resize(window, + window->saved_allocation.width, + window->saved_allocation.height); -static void -handle_surface_request_unset_fullscreen(void *data, struct xdg_surface *xdg_surface) -{ - struct window *window = data; - window_set_fullscreen(window, 0); + xdg_surface_ack_change_state(xdg_surface, state, value, serial); + window_schedule_redraw(window); } static void @@ -3889,10 +3890,7 @@ handle_surface_delete(void *data, struct xdg_surface *xdg_surface) static const struct xdg_surface_listener xdg_surface_listener = { handle_surface_configure, - handle_surface_request_set_maximized, - handle_surface_request_unset_maximized, - handle_surface_request_set_fullscreen, - handle_surface_request_unset_fullscreen, + handle_surface_change_state, handle_surface_activated, handle_surface_deactivated, handle_surface_delete, @@ -4125,39 +4123,6 @@ window_schedule_redraw(struct window *window) window_schedule_redraw_task(window); } -static void -configure_sync_callback(void *data, - struct wl_callback *callback, uint32_t time) -{ - struct window *window = data; - - DBG("scheduling redraw from maximize sync callback\n"); - - wl_callback_destroy(callback); - - window->redraw_task_scheduled = 0; - window_schedule_redraw_task(window); -} - -static struct wl_callback_listener configure_sync_callback_listener = { - configure_sync_callback, -}; - -static void -window_delay_redraw(struct window *window) -{ - struct wl_callback *callback; - - DBG("delay scheduled redraw for maximize configure\n"); - if (window->redraw_task_scheduled) - wl_list_remove(&window->redraw_task.link); - - window->redraw_task_scheduled = 1; - callback = wl_display_sync(window->display->display); - wl_callback_add_listener(callback, - &configure_sync_callback_listener, window); -} - int window_is_fullscreen(struct window *window) { @@ -4173,13 +4138,10 @@ window_set_fullscreen(struct window *window, int fullscreen) if (window->fullscreen == fullscreen) return; - window->fullscreen = fullscreen; - if (window->fullscreen) - xdg_surface_set_fullscreen(window->xdg_surface); - else - xdg_surface_unset_fullscreen(window->xdg_surface); - - window_delay_redraw(window); + xdg_surface_request_change_state(window->xdg_surface, + XDG_SURFACE_STATE_FULLSCREEN, + fullscreen ? 1 : 0, + 0); } int @@ -4197,13 +4159,10 @@ window_set_maximized(struct window *window, int maximized) if (window->maximized == maximized) return; - window->maximized = maximized; - if (window->maximized) - xdg_surface_set_maximized(window->xdg_surface); - else - xdg_surface_unset_maximized(window->xdg_surface); - - window_delay_redraw(window); + xdg_surface_request_change_state(window->xdg_surface, + XDG_SURFACE_STATE_MAXIMIZED, + maximized ? 1 : 0, + 0); } void diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 78119623..3b014627 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -161,8 +161,9 @@ 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; }; @@ -2288,8 +2289,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, @@ -2358,6 +2357,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 @@ -2416,8 +2418,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; } @@ -2459,6 +2459,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 @@ -3243,87 +3246,95 @@ xdg_surface_set_output(struct wl_client *client, } static void -xdg_surface_set_fullscreen(struct wl_client *client, - struct wl_resource *resource) +xdg_surface_set_fullscreen(struct shell_surface *shsurf, int serial) { - struct shell_surface *shsurf = wl_resource_get_user_data(resource); + shsurf->requested_state.fullscreen = true; + shsurf->state_requested = true; - if (shsurf->type != SHELL_SURFACE_TOPLEVEL) - return; + xdg_surface_send_change_state(shsurf->resource, + XDG_SURFACE_STATE_FULLSCREEN, 1, serial); - if (!shsurf->next_state.fullscreen) - set_fullscreen(shsurf, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, - 0, shsurf->recommended_output); + set_fullscreen(shsurf, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, shsurf->recommended_output); } static void -xdg_surface_unset_fullscreen(struct wl_client *client, - struct wl_resource *resource) +xdg_surface_unset_fullscreen(struct shell_surface *shsurf, int serial) { - struct shell_surface *shsurf = wl_resource_get_user_data(resource); - int32_t width, height; + shsurf->requested_state.fullscreen = false; + shsurf->state_requested = true; - if (shsurf->type != SHELL_SURFACE_TOPLEVEL) - return; + xdg_surface_send_change_state(shsurf->resource, + XDG_SURFACE_STATE_FULLSCREEN, 0, serial); +} - if (!shsurf->next_state.fullscreen) - return; +static void +xdg_surface_set_maximized(struct shell_surface *shsurf, int serial) +{ + shsurf->requested_state.maximized = true; + shsurf->state_requested = true; - shsurf->next_state.fullscreen = false; - shsurf->state_changed = true; + set_maximized(shsurf, NULL); - 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; - } + xdg_surface_send_change_state(shsurf->resource, + XDG_SURFACE_STATE_MAXIMIZED, 1, serial); +} +static void +xdg_surface_unset_maximized(struct shell_surface *shsurf, int serial) +{ + shsurf->requested_state.maximized = false; + shsurf->state_requested = true; - shsurf->client->send_configure(shsurf->surface, 0, width, height); + xdg_surface_send_change_state(shsurf->resource, + XDG_SURFACE_STATE_MAXIMIZED, 0, serial); } static void -xdg_surface_set_maximized(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); + /* 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.maximized) - set_maximized(shsurf, NULL); + switch (state) { + case XDG_SURFACE_STATE_MAXIMIZED: + if (value) + xdg_surface_set_maximized(shsurf, serial); + else + xdg_surface_unset_maximized(shsurf, serial); + break; + case XDG_SURFACE_STATE_FULLSCREEN: + if (value) + xdg_surface_set_fullscreen(shsurf, serial); + else + xdg_surface_unset_fullscreen(shsurf, serial); + break; + } } static void -xdg_surface_unset_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); - 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; + if (shsurf->state_requested) { + shsurf->next_state = shsurf->requested_state; + shsurf->state_changed = true; + shsurf->state_requested = false; } - - shsurf->client->send_configure(shsurf->surface, 0, width, height); } static const struct xdg_surface_interface xdg_surface_implementation = { @@ -3335,10 +3346,8 @@ static const struct xdg_surface_interface xdg_surface_implementation = { 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, NULL /* set_minimized */ }; @@ -3925,9 +3934,9 @@ maximize_binding(struct weston_seat *seat, uint32_t time, uint32_t button, void return; if (shsurf->state.maximized) - xdg_surface_send_request_unset_maximized(shsurf->resource); + xdg_surface_unset_maximized(shsurf, wl_display_next_serial(seat->compositor->wl_display)); else - xdg_surface_send_request_set_maximized(shsurf->resource); + xdg_surface_set_maximized(shsurf, wl_display_next_serial(seat->compositor->wl_display)); } static void @@ -3949,9 +3958,9 @@ fullscreen_binding(struct weston_seat *seat, uint32_t time, uint32_t button, voi return; if (shsurf->state.fullscreen) - xdg_surface_send_request_unset_fullscreen(shsurf->resource); + xdg_surface_unset_fullscreen(shsurf, wl_display_next_serial(seat->compositor->wl_display)); else - xdg_surface_send_request_set_fullscreen(shsurf->resource); + xdg_surface_set_fullscreen(shsurf, wl_display_next_serial(seat->compositor->wl_display)); } static void diff --git a/protocol/xdg-shell.xml b/protocol/xdg-shell.xml index 4a1d08ab..e690740a 100644 --- a/protocol/xdg-shell.xml +++ b/protocol/xdg-shell.xml @@ -275,113 +275,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 - - - - - 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. - - + + 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. + + - - - Unset the surface fullscreen state. + + + 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. - Same negotiation as set_fullscreen must be used. + 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. + - - - 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. + + + 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 - 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. + + + 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. - 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. + By this point, the state is confirmed, and the next attach should + contain the buffer drawn for the new state value. - 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. + 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.