shell: Centralize management of sending configure requests
Currently, there's a giant bug in how xdg-shell state management
is done. If a client calls set_fullscreen and then set_maximized,
it will get two configure events:
=> set_fullscreen
<= configure(800, 600, [STATE_FULLSCREEN])
=> set_maximized
<= configure(800, 560, [STATE_FULLSCREEN, STATE_MAXIMIZED])
Since fullscreen takes precedence over maximized, the client will
render full-screen at 800x600 first, and then 800x560 next. As
a result, the surface gets the wrong size.
This is because the code that sends out configure requests is
"immediate" -- when an app calls set_maximized, we immediately
send out the configure event that would have happened if we
transitioned immediately into maximized mode.
In wl_shell, this is correct behavior. However, in xdg-shell,
this is wrong. State needs to be more carefully managed in
xdg-shell, as states aren't exclusive.
Pull all the code that sends out configure events out and send
them centrally, based on Weston's on surface state management.
This should work with both wl_shell and xdg_shell's strategies.
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index 1704b84..293697f 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -162,7 +162,7 @@
const struct weston_shell_client *client;
- struct {
+ struct surface_state {
bool maximized;
bool fullscreen;
bool relative;
@@ -351,14 +351,64 @@
}
}
+static int
+get_output_panel_height(struct desktop_shell *shell,
+ struct weston_output *output)
+{
+ struct weston_view *view;
+ int panel_height = 0;
+
+ if (!output)
+ return 0;
+
+ wl_list_for_each(view, &shell->panel_layer.view_list, layer_link) {
+ if (view->surface->output == output) {
+ panel_height = view->surface->height;
+ break;
+ }
+ }
+
+ return panel_height;
+}
+
+static void
+send_configure_for_surface(struct shell_surface *shsurf)
+{
+ int32_t width, height;
+ struct surface_state *state;
+
+ if (shsurf->state_requested)
+ state = &shsurf->requested_state;
+ else if (shsurf->state_changed)
+ state = &shsurf->next_state;
+ else
+ state = &shsurf->state;
+
+ if (state->fullscreen) {
+ width = shsurf->output->width;
+ height = shsurf->output->height;
+ } else if (state->maximized) {
+ struct desktop_shell *shell;
+ uint32_t panel_height = 0;
+
+ shell = shell_surface_get_shell(shsurf);
+ panel_height = get_output_panel_height(shell, shsurf->output);
+
+ width = shsurf->output->width;
+ height = shsurf->output->height - panel_height;
+ } else {
+ width = 0;
+ height = 0;
+ }
+
+ shsurf->client->send_configure(shsurf->surface, width, height);
+}
+
static void
shell_surface_state_changed(struct shell_surface *shsurf)
{
- if (shell_surface_is_xdg_surface(shsurf)) {
- shsurf->client->send_configure(shsurf->surface,
- shsurf->surface->width,
- shsurf->surface->height);
- }
+ if (shell_surface_is_xdg_surface(shsurf))
+ send_configure_for_surface(shsurf);
}
static void
@@ -2122,26 +2172,6 @@
restore_output_mode(output);
}
-static int
-get_output_panel_height(struct desktop_shell *shell,
- struct weston_output *output)
-{
- struct weston_view *view;
- int panel_height = 0;
-
- if (!output)
- return 0;
-
- wl_list_for_each(view, &shell->panel_layer.view_list, layer_link) {
- if (view->surface->output == output) {
- panel_height = view->surface->height;
- break;
- }
- }
-
- return panel_height;
-}
-
/* The surface will be inserted into the list immediately after the link
* returned by this function (i.e. will be stacked immediately above the
* returned link). */
@@ -2343,19 +2373,13 @@
struct weston_output *output)
{
shell_surface_set_output(shsurf, output);
+ shsurf->type = SHELL_SURFACE_TOPLEVEL;
shsurf->fullscreen_output = shsurf->output;
shsurf->fullscreen.type = method;
shsurf->fullscreen.framerate = framerate;
- shsurf->type = SHELL_SURFACE_TOPLEVEL;
-
- shsurf->client->send_configure(shsurf->surface,
- shsurf->output->width,
- shsurf->output->height);
-
- /* The layer_link is updated in set_surface_type(),
- * called from configure. */
+ send_configure_for_surface(shsurf);
}
static void
@@ -2414,10 +2438,9 @@
shell_surface_set_parent(shsurf, NULL);
surface_clear_next_states(shsurf);
- set_fullscreen(shsurf, method, framerate, output);
-
shsurf->next_state.fullscreen = true;
shsurf->state_changed = true;
+ set_fullscreen(shsurf, method, framerate, output);
}
static void
@@ -2463,19 +2486,10 @@
set_maximized(struct shell_surface *shsurf,
struct weston_output *output)
{
- struct desktop_shell *shell;
- uint32_t panel_height = 0;
-
shell_surface_set_output(shsurf, output);
-
- shell = shell_surface_get_shell(shsurf);
- panel_height = get_output_panel_height(shell, shsurf->output);
-
- shsurf->client->send_configure(shsurf->surface,
- shsurf->output->width,
- shsurf->output->height - panel_height);
-
shsurf->type = SHELL_SURFACE_TOPLEVEL;
+
+ send_configure_for_surface(shsurf);
}
static void
@@ -3463,7 +3477,7 @@
shsurf->state_requested = true;
shsurf->requested_state.maximized = false;
- shsurf->client->send_configure(shsurf->surface, 0, 0);
+ send_configure_for_surface(shsurf);
}
static void
@@ -3497,7 +3511,7 @@
shsurf->state_requested = true;
shsurf->requested_state.fullscreen = false;
- shsurf->client->send_configure(shsurf->surface, 0, 0);
+ send_configure_for_surface(shsurf);
}
static void