diff options
Diffstat (limited to 'sway/desktop/xdg_shell.c')
-rw-r--r-- | sway/desktop/xdg_shell.c | 384 |
1 files changed, 204 insertions, 180 deletions
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 667fb9e5..7cdd97c8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include <wlr/util/edges.h> | 7 | #include <wlr/util/edges.h> |
8 | #include "log.h" | 8 | #include "log.h" |
9 | #include "sway/decoration.h" | 9 | #include "sway/decoration.h" |
10 | #include "sway/desktop.h" | 10 | #include "sway/scene_descriptor.h" |
11 | #include "sway/desktop/transaction.h" | 11 | #include "sway/desktop/transaction.h" |
12 | #include "sway/input/cursor.h" | 12 | #include "sway/input/cursor.h" |
13 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
@@ -19,64 +19,44 @@ | |||
19 | #include "sway/tree/workspace.h" | 19 | #include "sway/tree/workspace.h" |
20 | #include "sway/xdg_decoration.h" | 20 | #include "sway/xdg_decoration.h" |
21 | 21 | ||
22 | static const struct sway_view_child_impl popup_impl; | ||
23 | |||
24 | static void popup_get_root_coords(struct sway_view_child *child, | ||
25 | int *root_sx, int *root_sy) { | ||
26 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
27 | struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; | ||
28 | |||
29 | int x_offset = -child->view->geometry.x - surface->geometry.x; | ||
30 | int y_offset = -child->view->geometry.y - surface->geometry.y; | ||
31 | |||
32 | wlr_xdg_popup_get_toplevel_coords(surface->popup, | ||
33 | x_offset + surface->popup->geometry.x, | ||
34 | y_offset + surface->popup->geometry.y, | ||
35 | root_sx, root_sy); | ||
36 | } | ||
37 | |||
38 | static void popup_destroy(struct sway_view_child *child) { | ||
39 | if (!sway_assert(child->impl == &popup_impl, | ||
40 | "Expected an xdg_shell popup")) { | ||
41 | return; | ||
42 | } | ||
43 | struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; | ||
44 | wl_list_remove(&popup->new_popup.link); | ||
45 | wl_list_remove(&popup->destroy.link); | ||
46 | free(popup); | ||
47 | } | ||
48 | |||
49 | static const struct sway_view_child_impl popup_impl = { | ||
50 | .get_root_coords = popup_get_root_coords, | ||
51 | .destroy = popup_destroy, | ||
52 | }; | ||
53 | |||
54 | static struct sway_xdg_popup *popup_create( | 22 | static struct sway_xdg_popup *popup_create( |
55 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view); | 23 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view, |
24 | struct wlr_scene_tree *parent); | ||
56 | 25 | ||
57 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { | 26 | static void popup_handle_new_popup(struct wl_listener *listener, void *data) { |
58 | struct sway_xdg_popup *popup = | 27 | struct sway_xdg_popup *popup = |
59 | wl_container_of(listener, popup, new_popup); | 28 | wl_container_of(listener, popup, new_popup); |
60 | struct wlr_xdg_popup *wlr_popup = data; | 29 | struct wlr_xdg_popup *wlr_popup = data; |
61 | popup_create(wlr_popup, popup->child.view); | 30 | popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); |
62 | } | 31 | } |
63 | 32 | ||
64 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { | 33 | static void popup_handle_destroy(struct wl_listener *listener, void *data) { |
65 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); | 34 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy); |
66 | view_child_destroy(&popup->child); | 35 | |
36 | wl_list_remove(&popup->new_popup.link); | ||
37 | wl_list_remove(&popup->destroy.link); | ||
38 | wl_list_remove(&popup->surface_commit.link); | ||
39 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
40 | free(popup); | ||
67 | } | 41 | } |
68 | 42 | ||
69 | static void popup_unconstrain(struct sway_xdg_popup *popup) { | 43 | static void popup_unconstrain(struct sway_xdg_popup *popup) { |
70 | struct sway_view *view = popup->child.view; | 44 | struct sway_view *view = popup->view; |
71 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; | 45 | struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; |
72 | 46 | ||
73 | struct sway_output *output = view->container->workspace->output; | 47 | struct sway_workspace *workspace = view->container->pending.workspace; |
48 | if (!workspace) { | ||
49 | // is null if in the scratchpad | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | struct sway_output *output = workspace->output; | ||
74 | 54 | ||
75 | // the output box expressed in the coordinate system of the toplevel parent | 55 | // the output box expressed in the coordinate system of the toplevel parent |
76 | // of the popup | 56 | // of the popup |
77 | struct wlr_box output_toplevel_sx_box = { | 57 | struct wlr_box output_toplevel_sx_box = { |
78 | .x = output->lx - view->container->content_x, | 58 | .x = output->lx - view->container->pending.content_x + view->geometry.x, |
79 | .y = output->ly - view->container->content_y, | 59 | .y = output->ly - view->container->pending.content_y + view->geometry.y, |
80 | .width = output->width, | 60 | .width = output->width, |
81 | .height = output->height, | 61 | .height = output->height, |
82 | }; | 62 | }; |
@@ -84,32 +64,62 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { | |||
84 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); | 64 | wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); |
85 | } | 65 | } |
86 | 66 | ||
87 | static struct sway_xdg_popup *popup_create( | 67 | static void popup_handle_surface_commit(struct wl_listener *listener, void *data) { |
88 | struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { | 68 | struct sway_xdg_popup *popup = wl_container_of(listener, popup, surface_commit); |
69 | if (popup->wlr_xdg_popup->base->initial_commit) { | ||
70 | popup_unconstrain(popup); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, | ||
75 | struct sway_view *view, struct wlr_scene_tree *parent) { | ||
89 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; | 76 | struct wlr_xdg_surface *xdg_surface = wlr_popup->base; |
90 | 77 | ||
91 | struct sway_xdg_popup *popup = | 78 | struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); |
92 | calloc(1, sizeof(struct sway_xdg_popup)); | 79 | if (!popup) { |
93 | if (popup == NULL) { | ||
94 | return NULL; | 80 | return NULL; |
95 | } | 81 | } |
96 | view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); | ||
97 | popup->wlr_xdg_surface = xdg_surface; | ||
98 | 82 | ||
83 | popup->wlr_xdg_popup = wlr_popup; | ||
84 | popup->view = view; | ||
85 | |||
86 | popup->scene_tree = wlr_scene_tree_create(parent); | ||
87 | if (!popup->scene_tree) { | ||
88 | free(popup); | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | popup->xdg_surface_tree = wlr_scene_xdg_surface_create( | ||
93 | popup->scene_tree, xdg_surface); | ||
94 | if (!popup->xdg_surface_tree) { | ||
95 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
96 | free(popup); | ||
97 | return NULL; | ||
98 | } | ||
99 | |||
100 | if (!scene_descriptor_assign(&popup->scene_tree->node, | ||
101 | SWAY_SCENE_DESC_POPUP, popup)) { | ||
102 | sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); | ||
103 | wlr_scene_node_destroy(&popup->scene_tree->node); | ||
104 | free(popup); | ||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | popup->wlr_xdg_popup = xdg_surface->popup; | ||
109 | struct sway_xdg_shell_view *shell_view = | ||
110 | wl_container_of(view, shell_view, view); | ||
111 | xdg_surface->data = shell_view; | ||
112 | |||
113 | wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit); | ||
114 | popup->surface_commit.notify = popup_handle_surface_commit; | ||
99 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); | 115 | wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); |
100 | popup->new_popup.notify = popup_handle_new_popup; | 116 | popup->new_popup.notify = popup_handle_new_popup; |
101 | wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); | 117 | wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); |
102 | popup->destroy.notify = popup_handle_destroy; | 118 | popup->destroy.notify = popup_handle_destroy; |
103 | 119 | ||
104 | wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); | ||
105 | wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); | ||
106 | |||
107 | popup_unconstrain(popup); | ||
108 | |||
109 | return popup; | 120 | return popup; |
110 | } | 121 | } |
111 | 122 | ||
112 | |||
113 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( | 123 | static struct sway_xdg_shell_view *xdg_shell_view_from_view( |
114 | struct sway_view *view) { | 124 | struct sway_view *view) { |
115 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, | 125 | if (!sway_assert(view->type == SWAY_VIEW_XDG_SHELL, |
@@ -122,7 +132,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
122 | static void get_constraints(struct sway_view *view, double *min_width, | 132 | static void get_constraints(struct sway_view *view, double *min_width, |
123 | double *max_width, double *min_height, double *max_height) { | 133 | double *max_width, double *min_height, double *max_height) { |
124 | struct wlr_xdg_toplevel_state *state = | 134 | struct wlr_xdg_toplevel_state *state = |
125 | &view->wlr_xdg_surface->toplevel->current; | 135 | &view->wlr_xdg_toplevel->current; |
126 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | 136 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; |
127 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | 137 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; |
128 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | 138 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; |
@@ -136,9 +146,9 @@ static const char *get_string_prop(struct sway_view *view, | |||
136 | } | 146 | } |
137 | switch (prop) { | 147 | switch (prop) { |
138 | case VIEW_PROP_TITLE: | 148 | case VIEW_PROP_TITLE: |
139 | return view->wlr_xdg_surface->toplevel->title; | 149 | return view->wlr_xdg_toplevel->title; |
140 | case VIEW_PROP_APP_ID: | 150 | case VIEW_PROP_APP_ID: |
141 | return view->wlr_xdg_surface->toplevel->app_id; | 151 | return view->wlr_xdg_toplevel->app_id; |
142 | default: | 152 | default: |
143 | return NULL; | 153 | return NULL; |
144 | } | 154 | } |
@@ -151,50 +161,52 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, | |||
151 | if (xdg_shell_view == NULL) { | 161 | if (xdg_shell_view == NULL) { |
152 | return 0; | 162 | return 0; |
153 | } | 163 | } |
154 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); | 164 | return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, |
165 | width, height); | ||
155 | } | 166 | } |
156 | 167 | ||
157 | static void set_activated(struct sway_view *view, bool activated) { | 168 | static void set_activated(struct sway_view *view, bool activated) { |
158 | if (xdg_shell_view_from_view(view) == NULL) { | 169 | if (xdg_shell_view_from_view(view) == NULL) { |
159 | return; | 170 | return; |
160 | } | 171 | } |
161 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 172 | wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); |
162 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||
163 | wlr_xdg_toplevel_set_activated(surface, activated); | ||
164 | } | ||
165 | } | 173 | } |
166 | 174 | ||
167 | static void set_tiled(struct sway_view *view, bool tiled) { | 175 | static void set_tiled(struct sway_view *view, bool tiled) { |
168 | if (xdg_shell_view_from_view(view) == NULL) { | 176 | if (xdg_shell_view_from_view(view) == NULL) { |
169 | return; | 177 | return; |
170 | } | 178 | } |
171 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 179 | if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= |
172 | enum wlr_edges edges = WLR_EDGE_NONE; | 180 | XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { |
173 | if (tiled) { | 181 | enum wlr_edges edges = WLR_EDGE_NONE; |
174 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | | 182 | if (tiled) { |
175 | WLR_EDGE_BOTTOM; | 183 | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | |
184 | WLR_EDGE_BOTTOM; | ||
185 | } | ||
186 | wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); | ||
187 | } else { | ||
188 | // The version is too low for the tiled state; configure as maximized instead | ||
189 | // to stop the client from drawing decorations outside of the toplevel geometry. | ||
190 | wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); | ||
176 | } | 191 | } |
177 | wlr_xdg_toplevel_set_tiled(surface, edges); | ||
178 | } | 192 | } |
179 | 193 | ||
180 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { | 194 | static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
181 | if (xdg_shell_view_from_view(view) == NULL) { | 195 | if (xdg_shell_view_from_view(view) == NULL) { |
182 | return; | 196 | return; |
183 | } | 197 | } |
184 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 198 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
185 | wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); | ||
186 | } | 199 | } |
187 | 200 | ||
188 | static void set_resizing(struct sway_view *view, bool resizing) { | 201 | static void set_resizing(struct sway_view *view, bool resizing) { |
189 | if (xdg_shell_view_from_view(view) == NULL) { | 202 | if (xdg_shell_view_from_view(view) == NULL) { |
190 | return; | 203 | return; |
191 | } | 204 | } |
192 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 205 | wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); |
193 | wlr_xdg_toplevel_set_resizing(surface, resizing); | ||
194 | } | 206 | } |
195 | 207 | ||
196 | static bool wants_floating(struct sway_view *view) { | 208 | static bool wants_floating(struct sway_view *view) { |
197 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; | 209 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
198 | struct wlr_xdg_toplevel_state *state = &toplevel->current; | 210 | struct wlr_xdg_toplevel_state *state = &toplevel->current; |
199 | return (state->min_width != 0 && state->min_height != 0 | 211 | return (state->min_width != 0 && state->min_height != 0 |
200 | && (state->min_width == state->max_width | 212 | && (state->min_width == state->max_width |
@@ -202,35 +214,17 @@ static bool wants_floating(struct sway_view *view) { | |||
202 | || toplevel->parent; | 214 | || toplevel->parent; |
203 | } | 215 | } |
204 | 216 | ||
205 | static void for_each_surface(struct sway_view *view, | ||
206 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
207 | if (xdg_shell_view_from_view(view) == NULL) { | ||
208 | return; | ||
209 | } | ||
210 | wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, | ||
211 | user_data); | ||
212 | } | ||
213 | |||
214 | static void for_each_popup_surface(struct sway_view *view, | ||
215 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
216 | if (xdg_shell_view_from_view(view) == NULL) { | ||
217 | return; | ||
218 | } | ||
219 | wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, | ||
220 | user_data); | ||
221 | } | ||
222 | |||
223 | static bool is_transient_for(struct sway_view *child, | 217 | static bool is_transient_for(struct sway_view *child, |
224 | struct sway_view *ancestor) { | 218 | struct sway_view *ancestor) { |
225 | if (xdg_shell_view_from_view(child) == NULL) { | 219 | if (xdg_shell_view_from_view(child) == NULL) { |
226 | return false; | 220 | return false; |
227 | } | 221 | } |
228 | struct wlr_xdg_surface *surface = child->wlr_xdg_surface; | 222 | struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; |
229 | while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | 223 | while (toplevel) { |
230 | if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { | 224 | if (toplevel->parent == ancestor->wlr_xdg_toplevel) { |
231 | return true; | 225 | return true; |
232 | } | 226 | } |
233 | surface = surface->toplevel->parent; | 227 | toplevel = toplevel->parent; |
234 | } | 228 | } |
235 | return false; | 229 | return false; |
236 | } | 230 | } |
@@ -239,17 +233,13 @@ static void _close(struct sway_view *view) { | |||
239 | if (xdg_shell_view_from_view(view) == NULL) { | 233 | if (xdg_shell_view_from_view(view) == NULL) { |
240 | return; | 234 | return; |
241 | } | 235 | } |
242 | struct wlr_xdg_surface *surface = view->wlr_xdg_surface; | 236 | wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); |
243 | if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL | ||
244 | && surface->toplevel) { | ||
245 | wlr_xdg_toplevel_send_close(surface); | ||
246 | } | ||
247 | } | 237 | } |
248 | 238 | ||
249 | static void close_popups(struct sway_view *view) { | 239 | static void close_popups(struct sway_view *view) { |
250 | struct wlr_xdg_popup *popup, *tmp; | 240 | struct wlr_xdg_popup *popup, *tmp; |
251 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { | 241 | wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { |
252 | wlr_xdg_popup_destroy(popup->base); | 242 | wlr_xdg_popup_destroy(popup); |
253 | } | 243 | } |
254 | } | 244 | } |
255 | 245 | ||
@@ -271,8 +261,6 @@ static const struct sway_view_impl view_impl = { | |||
271 | .set_fullscreen = set_fullscreen, | 261 | .set_fullscreen = set_fullscreen, |
272 | .set_resizing = set_resizing, | 262 | .set_resizing = set_resizing, |
273 | .wants_floating = wants_floating, | 263 | .wants_floating = wants_floating, |
274 | .for_each_surface = for_each_surface, | ||
275 | .for_each_popup_surface = for_each_popup_surface, | ||
276 | .is_transient_for = is_transient_for, | 264 | .is_transient_for = is_transient_for, |
277 | .close = _close, | 265 | .close = _close, |
278 | .close_popups = close_popups, | 266 | .close_popups = close_popups, |
@@ -283,7 +271,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
283 | struct sway_xdg_shell_view *xdg_shell_view = | 271 | struct sway_xdg_shell_view *xdg_shell_view = |
284 | wl_container_of(listener, xdg_shell_view, commit); | 272 | wl_container_of(listener, xdg_shell_view, commit); |
285 | struct sway_view *view = &xdg_shell_view->view; | 273 | struct sway_view *view = &xdg_shell_view->view; |
286 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 274 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; |
275 | |||
276 | if (xdg_surface->initial_commit) { | ||
277 | if (view->xdg_decoration != NULL) { | ||
278 | set_xdg_decoration_mode(view->xdg_decoration); | ||
279 | } | ||
280 | // XXX: https://github.com/swaywm/sway/issues/2176 | ||
281 | wlr_xdg_surface_schedule_configure(xdg_surface); | ||
282 | return; | ||
283 | } | ||
284 | |||
285 | if (!xdg_surface->surface->mapped) { | ||
286 | return; | ||
287 | } | ||
287 | 288 | ||
288 | struct wlr_box new_geo; | 289 | struct wlr_box new_geo; |
289 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); | 290 | wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); |
@@ -293,22 +294,35 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
293 | new_geo.y != view->geometry.y; | 294 | new_geo.y != view->geometry.y; |
294 | 295 | ||
295 | if (new_size) { | 296 | if (new_size) { |
296 | // The view has unexpectedly sent a new size | 297 | // The client changed its surface size in this commit. For floating |
297 | desktop_damage_view(view); | 298 | // containers, we resize the container to match. For tiling containers, |
298 | view_update_size(view, new_geo.width, new_geo.height); | 299 | // we only recenter the surface. |
299 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); | 300 | memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); |
300 | desktop_damage_view(view); | 301 | if (container_is_floating(view->container)) { |
301 | transaction_commit_dirty(); | 302 | view_update_size(view); |
303 | // Only set the toplevel size the current container actually has a size. | ||
304 | if (view->container->current.width) { | ||
305 | wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, | ||
306 | view->geometry.height); | ||
307 | } | ||
308 | transaction_commit_dirty_client(); | ||
309 | } | ||
310 | |||
311 | view_center_and_clip_surface(view); | ||
302 | } | 312 | } |
303 | 313 | ||
304 | if (view->container->node.instruction) { | 314 | if (view->container->node.instruction) { |
305 | transaction_notify_view_ready_by_serial(view, | 315 | bool successful = transaction_notify_view_ready_by_serial(view, |
306 | xdg_surface->configure_serial); | 316 | xdg_surface->current.configure_serial); |
307 | } else if (new_size) { | 317 | |
308 | transaction_notify_view_ready_immediately(view); | 318 | // If we saved the view and this commit isn't what we're looking for |
319 | // that means the user will never actually see the buffers submitted to | ||
320 | // us here. Just send frame done events to these surfaces so they can | ||
321 | // commit another time for us. | ||
322 | if (view->saved_surface_tree && !successful) { | ||
323 | view_send_frame_done(view); | ||
324 | } | ||
309 | } | 325 | } |
310 | |||
311 | view_damage_from(view); | ||
312 | } | 326 | } |
313 | 327 | ||
314 | static void handle_set_title(struct wl_listener *listener, void *data) { | 328 | static void handle_set_title(struct wl_listener *listener, void *data) { |
@@ -330,31 +344,42 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { | |||
330 | struct sway_xdg_shell_view *xdg_shell_view = | 344 | struct sway_xdg_shell_view *xdg_shell_view = |
331 | wl_container_of(listener, xdg_shell_view, new_popup); | 345 | wl_container_of(listener, xdg_shell_view, new_popup); |
332 | struct wlr_xdg_popup *wlr_popup = data; | 346 | struct wlr_xdg_popup *wlr_popup = data; |
333 | popup_create(wlr_popup, &xdg_shell_view->view); | 347 | |
348 | struct sway_xdg_popup *popup = popup_create(wlr_popup, | ||
349 | &xdg_shell_view->view, root->layers.popup); | ||
350 | if (!popup) { | ||
351 | return; | ||
352 | } | ||
353 | |||
354 | int lx, ly; | ||
355 | wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly); | ||
356 | wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); | ||
357 | } | ||
358 | |||
359 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
360 | struct sway_xdg_shell_view *xdg_shell_view = | ||
361 | wl_container_of(listener, xdg_shell_view, request_maximize); | ||
362 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; | ||
363 | wlr_xdg_surface_schedule_configure(toplevel->base); | ||
334 | } | 364 | } |
335 | 365 | ||
336 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { | 366 | static void handle_request_fullscreen(struct wl_listener *listener, void *data) { |
337 | struct sway_xdg_shell_view *xdg_shell_view = | 367 | struct sway_xdg_shell_view *xdg_shell_view = |
338 | wl_container_of(listener, xdg_shell_view, request_fullscreen); | 368 | wl_container_of(listener, xdg_shell_view, request_fullscreen); |
339 | struct wlr_xdg_toplevel_set_fullscreen_event *e = data; | 369 | struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; |
340 | struct wlr_xdg_surface *xdg_surface = | ||
341 | xdg_shell_view->view.wlr_xdg_surface; | ||
342 | struct sway_view *view = &xdg_shell_view->view; | 370 | struct sway_view *view = &xdg_shell_view->view; |
343 | 371 | ||
344 | if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, | 372 | if (!toplevel->base->surface->mapped) { |
345 | "xdg_shell requested fullscreen of surface with role %i", | ||
346 | xdg_surface->role)) { | ||
347 | return; | ||
348 | } | ||
349 | if (!xdg_surface->mapped) { | ||
350 | return; | 373 | return; |
351 | } | 374 | } |
352 | 375 | ||
353 | struct sway_container *container = view->container; | 376 | struct sway_container *container = view->container; |
354 | if (e->fullscreen && e->output && e->output->data) { | 377 | struct wlr_xdg_toplevel_requested *req = &toplevel->requested; |
355 | struct sway_output *output = e->output->data; | 378 | if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) { |
379 | struct sway_output *output = req->fullscreen_output->data; | ||
356 | struct sway_workspace *ws = output_get_active_workspace(output); | 380 | struct sway_workspace *ws = output_get_active_workspace(output); |
357 | if (ws && !container_is_scratchpad_hidden(container)) { | 381 | if (ws && !container_is_scratchpad_hidden(container) && |
382 | container->pending.workspace != ws) { | ||
358 | if (container_is_floating(container)) { | 383 | if (container_is_floating(container)) { |
359 | workspace_add_floating(ws, container); | 384 | workspace_add_floating(ws, container); |
360 | } else { | 385 | } else { |
@@ -363,22 +388,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
363 | } | 388 | } |
364 | } | 389 | } |
365 | 390 | ||
366 | container_set_fullscreen(container, e->fullscreen); | 391 | container_set_fullscreen(container, req->fullscreen); |
367 | 392 | ||
368 | arrange_root(); | 393 | arrange_root(); |
369 | transaction_commit_dirty(); | 394 | transaction_commit_dirty(); |
370 | } | 395 | } |
371 | 396 | ||
372 | static void handle_request_maximize(struct wl_listener *listener, void *data) { | ||
373 | struct wlr_xdg_surface *surface = data; | ||
374 | wlr_xdg_surface_schedule_configure(surface); | ||
375 | } | ||
376 | |||
377 | static void handle_request_move(struct wl_listener *listener, void *data) { | 397 | static void handle_request_move(struct wl_listener *listener, void *data) { |
378 | struct sway_xdg_shell_view *xdg_shell_view = | 398 | struct sway_xdg_shell_view *xdg_shell_view = |
379 | wl_container_of(listener, xdg_shell_view, request_move); | 399 | wl_container_of(listener, xdg_shell_view, request_move); |
380 | struct sway_view *view = &xdg_shell_view->view; | 400 | struct sway_view *view = &xdg_shell_view->view; |
381 | if (!container_is_floating(view->container)) { | 401 | if (!container_is_floating(view->container) || |
402 | view->container->pending.fullscreen_mode) { | ||
382 | return; | 403 | return; |
383 | } | 404 | } |
384 | struct wlr_xdg_toplevel_move_event *e = data; | 405 | struct wlr_xdg_toplevel_move_event *e = data; |
@@ -413,10 +434,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
413 | 434 | ||
414 | view_unmap(view); | 435 | view_unmap(view); |
415 | 436 | ||
416 | wl_list_remove(&xdg_shell_view->commit.link); | ||
417 | wl_list_remove(&xdg_shell_view->new_popup.link); | 437 | wl_list_remove(&xdg_shell_view->new_popup.link); |
418 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
419 | wl_list_remove(&xdg_shell_view->request_maximize.link); | 438 | wl_list_remove(&xdg_shell_view->request_maximize.link); |
439 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | ||
420 | wl_list_remove(&xdg_shell_view->request_move.link); | 440 | wl_list_remove(&xdg_shell_view->request_move.link); |
421 | wl_list_remove(&xdg_shell_view->request_resize.link); | 441 | wl_list_remove(&xdg_shell_view->request_resize.link); |
422 | wl_list_remove(&xdg_shell_view->set_title.link); | 442 | wl_list_remove(&xdg_shell_view->set_title.link); |
@@ -427,62 +447,61 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
427 | struct sway_xdg_shell_view *xdg_shell_view = | 447 | struct sway_xdg_shell_view *xdg_shell_view = |
428 | wl_container_of(listener, xdg_shell_view, map); | 448 | wl_container_of(listener, xdg_shell_view, map); |
429 | struct sway_view *view = &xdg_shell_view->view; | 449 | struct sway_view *view = &xdg_shell_view->view; |
430 | struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; | 450 | struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; |
431 | 451 | ||
432 | view->natural_width = view->wlr_xdg_surface->geometry.width; | 452 | view->natural_width = toplevel->base->current.geometry.width; |
433 | view->natural_height = view->wlr_xdg_surface->geometry.height; | 453 | view->natural_height = toplevel->base->current.geometry.height; |
434 | if (!view->natural_width && !view->natural_height) { | 454 | if (!view->natural_width && !view->natural_height) { |
435 | view->natural_width = view->wlr_xdg_surface->surface->current.width; | 455 | view->natural_width = toplevel->base->surface->current.width; |
436 | view->natural_height = view->wlr_xdg_surface->surface->current.height; | 456 | view->natural_height = toplevel->base->surface->current.height; |
437 | } | 457 | } |
438 | 458 | ||
439 | bool csd = false; | 459 | bool csd = false; |
440 | 460 | ||
441 | if (!view->xdg_decoration) { | 461 | if (view->xdg_decoration) { |
462 | enum wlr_xdg_toplevel_decoration_v1_mode mode = | ||
463 | view->xdg_decoration->wlr_xdg_decoration->requested_mode; | ||
464 | csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; | ||
465 | } else { | ||
442 | struct sway_server_decoration *deco = | 466 | struct sway_server_decoration *deco = |
443 | decoration_from_surface(xdg_surface->surface); | 467 | decoration_from_surface(toplevel->base->surface); |
444 | csd = !deco || deco->wlr_server_decoration->mode == | 468 | csd = !deco || deco->wlr_server_decoration->mode == |
445 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; | 469 | WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; |
446 | |||
447 | } | 470 | } |
448 | 471 | ||
449 | view_map(view, view->wlr_xdg_surface->surface, | 472 | view_map(view, toplevel->base->surface, |
450 | xdg_surface->toplevel->client_pending.fullscreen, | 473 | toplevel->requested.fullscreen, |
451 | xdg_surface->toplevel->client_pending.fullscreen_output, | 474 | toplevel->requested.fullscreen_output, |
452 | csd); | 475 | csd); |
453 | 476 | ||
454 | transaction_commit_dirty(); | 477 | transaction_commit_dirty(); |
455 | 478 | ||
456 | xdg_shell_view->commit.notify = handle_commit; | ||
457 | wl_signal_add(&xdg_surface->surface->events.commit, | ||
458 | &xdg_shell_view->commit); | ||
459 | |||
460 | xdg_shell_view->new_popup.notify = handle_new_popup; | 479 | xdg_shell_view->new_popup.notify = handle_new_popup; |
461 | wl_signal_add(&xdg_surface->events.new_popup, | 480 | wl_signal_add(&toplevel->base->events.new_popup, |
462 | &xdg_shell_view->new_popup); | 481 | &xdg_shell_view->new_popup); |
463 | 482 | ||
464 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
465 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | ||
466 | &xdg_shell_view->request_fullscreen); | ||
467 | |||
468 | xdg_shell_view->request_maximize.notify = handle_request_maximize; | 483 | xdg_shell_view->request_maximize.notify = handle_request_maximize; |
469 | wl_signal_add(&xdg_surface->toplevel->events.request_maximize, | 484 | wl_signal_add(&toplevel->events.request_maximize, |
470 | &xdg_shell_view->request_maximize); | 485 | &xdg_shell_view->request_maximize); |
471 | 486 | ||
487 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | ||
488 | wl_signal_add(&toplevel->events.request_fullscreen, | ||
489 | &xdg_shell_view->request_fullscreen); | ||
490 | |||
472 | xdg_shell_view->request_move.notify = handle_request_move; | 491 | xdg_shell_view->request_move.notify = handle_request_move; |
473 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | 492 | wl_signal_add(&toplevel->events.request_move, |
474 | &xdg_shell_view->request_move); | 493 | &xdg_shell_view->request_move); |
475 | 494 | ||
476 | xdg_shell_view->request_resize.notify = handle_request_resize; | 495 | xdg_shell_view->request_resize.notify = handle_request_resize; |
477 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | 496 | wl_signal_add(&toplevel->events.request_resize, |
478 | &xdg_shell_view->request_resize); | 497 | &xdg_shell_view->request_resize); |
479 | 498 | ||
480 | xdg_shell_view->set_title.notify = handle_set_title; | 499 | xdg_shell_view->set_title.notify = handle_set_title; |
481 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | 500 | wl_signal_add(&toplevel->events.set_title, |
482 | &xdg_shell_view->set_title); | 501 | &xdg_shell_view->set_title); |
483 | 502 | ||
484 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | 503 | xdg_shell_view->set_app_id.notify = handle_set_app_id; |
485 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | 504 | wl_signal_add(&toplevel->events.set_app_id, |
486 | &xdg_shell_view->set_app_id); | 505 | &xdg_shell_view->set_app_id); |
487 | } | 506 | } |
488 | 507 | ||
@@ -496,7 +515,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
496 | wl_list_remove(&xdg_shell_view->destroy.link); | 515 | wl_list_remove(&xdg_shell_view->destroy.link); |
497 | wl_list_remove(&xdg_shell_view->map.link); | 516 | wl_list_remove(&xdg_shell_view->map.link); |
498 | wl_list_remove(&xdg_shell_view->unmap.link); | 517 | wl_list_remove(&xdg_shell_view->unmap.link); |
499 | view->wlr_xdg_surface = NULL; | 518 | wl_list_remove(&xdg_shell_view->commit.link); |
519 | view->wlr_xdg_toplevel = NULL; | ||
500 | if (view->xdg_decoration) { | 520 | if (view->xdg_decoration) { |
501 | view->xdg_decoration->view = NULL; | 521 | view->xdg_decoration->view = NULL; |
502 | } | 522 | } |
@@ -508,17 +528,12 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
508 | return xdg_surface->data; | 528 | return xdg_surface->data; |
509 | } | 529 | } |
510 | 530 | ||
511 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | 531 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { |
512 | struct wlr_xdg_surface *xdg_surface = data; | 532 | struct wlr_xdg_toplevel *xdg_toplevel = data; |
513 | |||
514 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
515 | sway_log(SWAY_DEBUG, "New xdg_shell popup"); | ||
516 | return; | ||
517 | } | ||
518 | 533 | ||
519 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", | 534 | sway_log(SWAY_DEBUG, "New xdg_shell toplevel title='%s' app_id='%s'", |
520 | xdg_surface->toplevel->title, xdg_surface->toplevel->app_id); | 535 | xdg_toplevel->title, xdg_toplevel->app_id); |
521 | wlr_xdg_surface_ping(xdg_surface); | 536 | wlr_xdg_surface_ping(xdg_toplevel->base); |
522 | 537 | ||
523 | struct sway_xdg_shell_view *xdg_shell_view = | 538 | struct sway_xdg_shell_view *xdg_shell_view = |
524 | calloc(1, sizeof(struct sway_xdg_shell_view)); | 539 | calloc(1, sizeof(struct sway_xdg_shell_view)); |
@@ -526,17 +541,26 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
526 | return; | 541 | return; |
527 | } | 542 | } |
528 | 543 | ||
529 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 544 | if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { |
530 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 545 | free(xdg_shell_view); |
546 | return; | ||
547 | } | ||
548 | xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel; | ||
531 | 549 | ||
532 | xdg_shell_view->map.notify = handle_map; | 550 | xdg_shell_view->map.notify = handle_map; |
533 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 551 | wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map); |
534 | 552 | ||
535 | xdg_shell_view->unmap.notify = handle_unmap; | 553 | xdg_shell_view->unmap.notify = handle_unmap; |
536 | wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); | 554 | wl_signal_add(&xdg_toplevel->base->surface->events.unmap, &xdg_shell_view->unmap); |
555 | |||
556 | xdg_shell_view->commit.notify = handle_commit; | ||
557 | wl_signal_add(&xdg_toplevel->base->surface->events.commit, | ||
558 | &xdg_shell_view->commit); | ||
537 | 559 | ||
538 | xdg_shell_view->destroy.notify = handle_destroy; | 560 | xdg_shell_view->destroy.notify = handle_destroy; |
539 | wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); | 561 | wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); |
562 | |||
563 | wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); | ||
540 | 564 | ||
541 | xdg_surface->data = xdg_shell_view; | 565 | xdg_toplevel->base->data = xdg_shell_view; |
542 | } | 566 | } |