aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/config.h7
-rw-r--r--include/sway/tree/view.h4
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/popup_during_fullscreen.c25
-rw-r--r--sway/config.c1
-rw-r--r--sway/desktop/output.c11
-rw-r--r--sway/desktop/render.c11
-rw-r--r--sway/desktop/xdg_shell.c29
-rw-r--r--sway/desktop/xdg_shell_v6.c28
-rw-r--r--sway/desktop/xwayland.c29
-rw-r--r--sway/input/cursor.c16
-rw-r--r--sway/input/seat.c9
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway.5.scd6
-rw-r--r--sway/tree/view.c15
16 files changed, 192 insertions, 2 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 21b8b87a..48228a98 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -154,6 +154,7 @@ sway_cmd cmd_new_window;
154sway_cmd cmd_no_focus; 154sway_cmd cmd_no_focus;
155sway_cmd cmd_output; 155sway_cmd cmd_output;
156sway_cmd cmd_permit; 156sway_cmd cmd_permit;
157sway_cmd cmd_popup_during_fullscreen;
157sway_cmd cmd_reject; 158sway_cmd cmd_reject;
158sway_cmd cmd_reload; 159sway_cmd cmd_reload;
159sway_cmd cmd_rename; 160sway_cmd cmd_rename;
diff --git a/include/sway/config.h b/include/sway/config.h
index 0e51fbfb..00b5f25b 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -256,6 +256,12 @@ enum edge_border_types {
256 E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */ 256 E_SMART_NO_GAPS, /**< hide both if one window and gaps to edge is zero */
257}; 257};
258 258
259enum sway_popup_during_fullscreen {
260 POPUP_SMART,
261 POPUP_IGNORE,
262 POPUP_LEAVE,
263};
264
259enum command_context { 265enum command_context {
260 CONTEXT_CONFIG = 1, 266 CONTEXT_CONFIG = 1,
261 CONTEXT_BINDING = 2, 267 CONTEXT_BINDING = 2,
@@ -355,6 +361,7 @@ struct sway_config {
355 bool pango_markup; 361 bool pango_markup;
356 size_t urgent_timeout; 362 size_t urgent_timeout;
357 enum sway_fowa focus_on_window_activation; 363 enum sway_fowa focus_on_window_activation;
364 enum sway_popup_during_fullscreen popup_during_fullscreen;
358 365
359 // Flags 366 // Flags
360 bool focus_follows_mouse; 367 bool focus_follows_mouse;
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 028be536..eb1e98e1 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -49,6 +49,8 @@ struct sway_view_impl {
49 wlr_surface_iterator_func_t iterator, void *user_data); 49 wlr_surface_iterator_func_t iterator, void *user_data);
50 void (*for_each_popup)(struct sway_view *view, 50 void (*for_each_popup)(struct sway_view *view,
51 wlr_surface_iterator_func_t iterator, void *user_data); 51 wlr_surface_iterator_func_t iterator, void *user_data);
52 bool (*is_transient_for)(struct sway_view *child,
53 struct sway_view *ancestor);
52 void (*close)(struct sway_view *view); 54 void (*close)(struct sway_view *view);
53 void (*close_popups)(struct sway_view *view); 55 void (*close_popups)(struct sway_view *view);
54 void (*destroy)(struct sway_view *view); 56 void (*destroy)(struct sway_view *view);
@@ -396,4 +398,6 @@ void view_remove_saved_buffer(struct sway_view *view);
396 398
397void view_save_buffer(struct sway_view *view); 399void view_save_buffer(struct sway_view *view);
398 400
401bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
402
399#endif 403#endif
diff --git a/sway/commands.c b/sway/commands.c
index 780cd7d6..8db1df01 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -109,6 +109,7 @@ static struct cmd_handler handlers[] = {
109 { "new_window", cmd_default_border }, 109 { "new_window", cmd_default_border },
110 { "no_focus", cmd_no_focus }, 110 { "no_focus", cmd_no_focus },
111 { "output", cmd_output }, 111 { "output", cmd_output },
112 { "popup_during_fullscreen", cmd_popup_during_fullscreen },
112 { "raise_floating", cmd_raise_floating }, 113 { "raise_floating", cmd_raise_floating },
113 { "seat", cmd_seat }, 114 { "seat", cmd_seat },
114 { "set", cmd_set }, 115 { "set", cmd_set },
diff --git a/sway/commands/popup_during_fullscreen.c b/sway/commands/popup_during_fullscreen.c
new file mode 100644
index 00000000..da1904b6
--- /dev/null
+++ b/sway/commands/popup_during_fullscreen.c
@@ -0,0 +1,25 @@
1#include <strings.h>
2#include "sway/commands.h"
3#include "sway/config.h"
4
5struct cmd_results *cmd_popup_during_fullscreen(int argc, char **argv) {
6 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "popup_during_fullscreen",
8 EXPECTED_EQUAL_TO, 1))) {
9 return error;
10 }
11
12 if (strcasecmp(argv[0], "smart") == 0) {
13 config->popup_during_fullscreen = POPUP_SMART;
14 } else if (strcasecmp(argv[0], "ignore") == 0) {
15 config->popup_during_fullscreen = POPUP_IGNORE;
16 } else if (strcasecmp(argv[0], "leave_fullscreen") == 0) {
17 config->popup_during_fullscreen = POPUP_LEAVE;
18 } else {
19 return cmd_results_new(CMD_INVALID, "popup_during_fullscreen",
20 "Expected "
21 "'popup_during_fullscreen smart|ignore|leave_fullscreen'");
22 }
23
24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
25}
diff --git a/sway/config.c b/sway/config.c
index 8f8ed438..070b15c8 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -212,6 +212,7 @@ static void config_defaults(struct sway_config *config) {
212 if (!(config->font = strdup("monospace 10"))) goto cleanup; 212 if (!(config->font = strdup("monospace 10"))) goto cleanup;
213 config->font_height = 17; // height of monospace 10 213 config->font_height = 17; // height of monospace 10
214 config->urgent_timeout = 500; 214 config->urgent_timeout = 500;
215 config->popup_during_fullscreen = POPUP_SMART;
215 216
216 // floating view 217 // floating view
217 config->floating_maximum_width = 0; 218 config->floating_maximum_width = 0;
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index cfb5a710..0bcdcac1 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -329,6 +329,17 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
329 workspace->current.fullscreen, &data); 329 workspace->current.fullscreen, &data);
330 container_for_each_child(workspace->current.fullscreen, 330 container_for_each_child(workspace->current.fullscreen,
331 send_frame_done_container_iterator, &data); 331 send_frame_done_container_iterator, &data);
332 if (config->popup_during_fullscreen == POPUP_SMART &&
333 workspace->current.fullscreen->view) {
334 for (int i = 0; i < workspace->current.floating->length; ++i) {
335 struct sway_container *floater =
336 workspace->current.floating->items[i];
337 if (floater->view && view_is_transient_for(floater->view,
338 workspace->current.fullscreen->view)) {
339 send_frame_done_container_iterator(floater, &data);
340 }
341 }
342 }
332#ifdef HAVE_XWAYLAND 343#ifdef HAVE_XWAYLAND
333 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when); 344 send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
334#endif 345#endif
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index c8b08a58..c2a0d29f 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -961,6 +961,17 @@ void output_render(struct sway_output *output, struct timespec *when,
961 render_container(output, damage, fullscreen_con, 961 render_container(output, damage, fullscreen_con,
962 fullscreen_con->current.focused); 962 fullscreen_con->current.focused);
963 } 963 }
964
965 if (config->popup_during_fullscreen == POPUP_SMART &&
966 fullscreen_con->view) {
967 for (int i = 0; i < workspace->floating->length; ++i) {
968 struct sway_container *floater = workspace->floating->items[i];
969 if (floater->view && view_is_transient_for(
970 floater->view, fullscreen_con->view)) {
971 render_floating_container(output, damage, floater);
972 }
973 }
974 }
964#ifdef HAVE_XWAYLAND 975#ifdef HAVE_XWAYLAND
965 render_unmanaged(output, damage, &root->xwayland_unmanaged); 976 render_unmanaged(output, damage, &root->xwayland_unmanaged);
966#endif 977#endif
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index a8b527a7..54831679 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -192,6 +192,21 @@ static void for_each_popup(struct sway_view *view,
192 wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); 192 wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data);
193} 193}
194 194
195static bool is_transient_for(struct sway_view *child,
196 struct sway_view *ancestor) {
197 if (xdg_shell_view_from_view(child) == NULL) {
198 return false;
199 }
200 struct wlr_xdg_surface *surface = child->wlr_xdg_surface;
201 while (surface) {
202 if (surface->toplevel->parent == ancestor->wlr_xdg_surface) {
203 return true;
204 }
205 surface = surface->toplevel->parent;
206 }
207 return false;
208}
209
195static void _close(struct sway_view *view) { 210static void _close(struct sway_view *view) {
196 if (xdg_shell_view_from_view(view) == NULL) { 211 if (xdg_shell_view_from_view(view) == NULL) {
197 return; 212 return;
@@ -233,6 +248,7 @@ static const struct sway_view_impl view_impl = {
233 .wants_floating = wants_floating, 248 .wants_floating = wants_floating,
234 .for_each_surface = for_each_surface, 249 .for_each_surface = for_each_surface,
235 .for_each_popup = for_each_popup, 250 .for_each_popup = for_each_popup,
251 .is_transient_for = is_transient_for,
236 .close = _close, 252 .close = _close,
237 .close_popups = close_popups, 253 .close_popups = close_popups,
238 .destroy = destroy, 254 .destroy = destroy,
@@ -385,6 +401,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
385 view_update_csd_from_client(view, csd); 401 view_update_csd_from_client(view, csd);
386 } 402 }
387 403
404 if (config->popup_during_fullscreen == POPUP_LEAVE &&
405 view->container->workspace &&
406 view->container->workspace->fullscreen &&
407 xdg_surface->toplevel->parent) {
408 struct wlr_xdg_surface *psurface = xdg_surface->toplevel->parent;
409 struct sway_xdg_shell_view *parent = psurface->data;
410 struct sway_view *sway_view = &parent->view;
411 if (sway_view->container && sway_view->container->is_fullscreen) {
412 container_set_fullscreen(sway_view->container, false);
413 }
414 }
415
388 if (xdg_surface->toplevel->client_pending.fullscreen) { 416 if (xdg_surface->toplevel->client_pending.fullscreen) {
389 container_set_fullscreen(view->container, true); 417 container_set_fullscreen(view->container, true);
390 arrange_workspace(view->container->workspace); 418 arrange_workspace(view->container->workspace);
@@ -395,6 +423,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
395 arrange_workspace(view->container->workspace); 423 arrange_workspace(view->container->workspace);
396 } 424 }
397 } 425 }
426
398 transaction_commit_dirty(); 427 transaction_commit_dirty();
399 428
400 xdg_shell_view->commit.notify = handle_commit; 429 xdg_shell_view->commit.notify = handle_commit;
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index a7838c0f..dacfca02 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -189,6 +189,21 @@ static void for_each_popup(struct sway_view *view,
189 user_data); 189 user_data);
190} 190}
191 191
192static bool is_transient_for(struct sway_view *child,
193 struct sway_view *ancestor) {
194 if (xdg_shell_v6_view_from_view(child) == NULL) {
195 return false;
196 }
197 struct wlr_xdg_surface_v6 *surface = child->wlr_xdg_surface_v6;
198 while (surface) {
199 if (surface->toplevel->parent == ancestor->wlr_xdg_surface_v6) {
200 return true;
201 }
202 surface = surface->toplevel->parent;
203 }
204 return false;
205}
206
192static void _close(struct sway_view *view) { 207static void _close(struct sway_view *view) {
193 if (xdg_shell_v6_view_from_view(view) == NULL) { 208 if (xdg_shell_v6_view_from_view(view) == NULL) {
194 return; 209 return;
@@ -230,6 +245,7 @@ static const struct sway_view_impl view_impl = {
230 .wants_floating = wants_floating, 245 .wants_floating = wants_floating,
231 .for_each_surface = for_each_surface, 246 .for_each_surface = for_each_surface,
232 .for_each_popup = for_each_popup, 247 .for_each_popup = for_each_popup,
248 .is_transient_for = is_transient_for,
233 .close = _close, 249 .close = _close,
234 .close_popups = close_popups, 250 .close_popups = close_popups,
235 .destroy = destroy, 251 .destroy = destroy,
@@ -380,6 +396,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
380 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; 396 WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
381 view_update_csd_from_client(view, csd); 397 view_update_csd_from_client(view, csd);
382 398
399 if (config->popup_during_fullscreen == POPUP_LEAVE &&
400 view->container->workspace &&
401 view->container->workspace->fullscreen &&
402 xdg_surface->toplevel->parent) {
403 struct wlr_xdg_surface_v6 *psurface = xdg_surface->toplevel->parent;
404 struct sway_xdg_shell_v6_view *parent = psurface->data;
405 struct sway_view *sway_view = &parent->view;
406 if (sway_view->container && sway_view->container->is_fullscreen) {
407 container_set_fullscreen(sway_view->container, false);
408 }
409 }
410
383 if (xdg_surface->toplevel->client_pending.fullscreen) { 411 if (xdg_surface->toplevel->client_pending.fullscreen) {
384 container_set_fullscreen(view->container, true); 412 container_set_fullscreen(view->container, true);
385 arrange_workspace(view->container->workspace); 413 arrange_workspace(view->container->workspace);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 4c710f7e..80489f93 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -15,6 +15,7 @@
15#include "sway/tree/arrange.h" 15#include "sway/tree/arrange.h"
16#include "sway/tree/container.h" 16#include "sway/tree/container.h"
17#include "sway/tree/view.h" 17#include "sway/tree/view.h"
18#include "sway/tree/workspace.h"
18 19
19static const char *atom_map[ATOM_LAST] = { 20static const char *atom_map[ATOM_LAST] = {
20 "_NET_WM_WINDOW_TYPE_NORMAL", 21 "_NET_WM_WINDOW_TYPE_NORMAL",
@@ -253,6 +254,21 @@ static void handle_set_decorations(struct wl_listener *listener, void *data) {
253 view_update_csd_from_client(view, csd); 254 view_update_csd_from_client(view, csd);
254} 255}
255 256
257static bool is_transient_for(struct sway_view *child,
258 struct sway_view *ancestor) {
259 if (xwayland_view_from_view(child) == NULL) {
260 return false;
261 }
262 struct wlr_xwayland_surface *surface = child->wlr_xwayland_surface;
263 while (surface) {
264 if (surface->parent == ancestor->wlr_xwayland_surface) {
265 return true;
266 }
267 surface = surface->parent;
268 }
269 return false;
270}
271
256static void _close(struct sway_view *view) { 272static void _close(struct sway_view *view) {
257 if (xwayland_view_from_view(view) == NULL) { 273 if (xwayland_view_from_view(view) == NULL) {
258 return; 274 return;
@@ -276,6 +292,7 @@ static const struct sway_view_impl view_impl = {
276 .set_tiled = set_tiled, 292 .set_tiled = set_tiled,
277 .set_fullscreen = set_fullscreen, 293 .set_fullscreen = set_fullscreen,
278 .wants_floating = wants_floating, 294 .wants_floating = wants_floating,
295 .is_transient_for = is_transient_for,
279 .close = _close, 296 .close = _close,
280 .destroy = destroy, 297 .destroy = destroy,
281}; 298};
@@ -390,6 +407,18 @@ static void handle_map(struct wl_listener *listener, void *data) {
390 // Put it back into the tree 407 // Put it back into the tree
391 view_map(view, xsurface->surface); 408 view_map(view, xsurface->surface);
392 409
410 if (config->popup_during_fullscreen == POPUP_LEAVE &&
411 view->container->workspace &&
412 view->container->workspace->fullscreen &&
413 xsurface->parent) {
414 struct wlr_xwayland_surface *psurface = xsurface->parent;
415 struct sway_xwayland_view *parent = psurface->data;
416 struct sway_view *sway_view = &parent->view;
417 if (sway_view->container && sway_view->container->is_fullscreen) {
418 container_set_fullscreen(sway_view->container, false);
419 }
420 }
421
393 if (xsurface->fullscreen) { 422 if (xsurface->fullscreen) {
394 container_set_fullscreen(view->container, true); 423 container_set_fullscreen(view->container, true);
395 arrange_workspace(view->container->workspace); 424 arrange_workspace(view->container->workspace);
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 331c6c7e..08eeb812 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -98,6 +98,22 @@ static struct sway_node *node_at_coords(
98 return NULL; 98 return NULL;
99 } 99 }
100 if (ws->fullscreen) { 100 if (ws->fullscreen) {
101 // Try transient containers
102 if (config->popup_during_fullscreen == POPUP_SMART &&
103 ws->fullscreen->view) {
104 for (int i = 0; i < ws->floating->length; ++i) {
105 struct sway_container *floater = ws->floating->items[i];
106 if (floater->view && view_is_transient_for(
107 floater->view, ws->fullscreen->view)) {
108 struct sway_container *con = tiling_container_at(
109 &floater->node, lx, ly, surface, sx, sy);
110 if (con) {
111 return &con->node;
112 }
113 }
114 }
115 }
116 // Try fullscreen container
101 struct sway_container *con = 117 struct sway_container *con =
102 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); 118 tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
103 if (con) { 119 if (con) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index f5cb2f9e..690b59e6 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -655,7 +655,14 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
655 // Deny setting focus to a view which is hidden by a fullscreen container 655 // Deny setting focus to a view which is hidden by a fullscreen container
656 if (new_workspace && new_workspace->fullscreen && container && 656 if (new_workspace && new_workspace->fullscreen && container &&
657 !container_is_fullscreen_or_child(container)) { 657 !container_is_fullscreen_or_child(container)) {
658 return; 658 // Unless it's a transient container
659 bool is_transient = new_workspace->fullscreen->view &&
660 config->popup_during_fullscreen == POPUP_SMART &&
661 container->view && view_is_transient_for(
662 container->view, new_workspace->fullscreen->view);
663 if (!is_transient) {
664 return;
665 }
659 } 666 }
660 667
661 struct sway_output *last_output = last_workspace ? 668 struct sway_output *last_output = last_workspace ?
diff --git a/sway/meson.build b/sway/meson.build
index 8ab28869..c7fc9697 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -70,6 +70,7 @@ sway_sources = files(
70 'commands/no_focus.c', 70 'commands/no_focus.c',
71 'commands/nop.c', 71 'commands/nop.c',
72 'commands/output.c', 72 'commands/output.c',
73 'commands/popup_during_fullscreen.c',
73 'commands/reload.c', 74 'commands/reload.c',
74 'commands/rename.c', 75 'commands/rename.c',
75 'commands/resize.c', 76 'commands/resize.c',
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 3fda6cef..387edf54 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -549,6 +549,12 @@ You may combine output commands into one, like so:
549You can get a list of output names with *swaymsg -t get\_outputs*. You may also 549You can get a list of output names with *swaymsg -t get\_outputs*. You may also
550match any output by using the output name "\*". 550match any output by using the output name "\*".
551 551
552*popup\_during\_fullscreen* smart|ignore|leave\_fullscreen
553 Determines what to do when a fullscreen view opens a dialog.
554 If _smart_ (the default), the dialog will be displayed. If _ignore_, the
555 dialog will not be rendered. If _leave\_fullscreen_, the view will exit
556 fullscreen mode and the dialog will be rendered.
557
552*set* $<name> <value> 558*set* $<name> <value>
553 Sets variable $_name_ to _value_. You can use the new variable in the 559 Sets variable $_name_ to _value_. You can use the new variable in the
554 arguments of future commands. When the variable is used, it can be escaped 560 arguments of future commands. When the variable is used, it can be escaped
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 73ce55ac..edf771c1 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -1042,7 +1042,14 @@ bool view_is_visible(struct sway_view *view) {
1042 // Check view isn't hidden by another fullscreen view 1042 // Check view isn't hidden by another fullscreen view
1043 if (workspace->fullscreen && 1043 if (workspace->fullscreen &&
1044 !container_is_fullscreen_or_child(view->container)) { 1044 !container_is_fullscreen_or_child(view->container)) {
1045 return false; 1045 // However, if we're transient for the fullscreen view and we allow
1046 // "popups" during fullscreen then it might be visible
1047 bool is_transient = config->popup_during_fullscreen == POPUP_SMART &&
1048 workspace->fullscreen->view &&
1049 view_is_transient_for(view, workspace->fullscreen->view);
1050 if (!is_transient) {
1051 return false;
1052 }
1046 } 1053 }
1047 return true; 1054 return true;
1048} 1055}
@@ -1095,3 +1102,9 @@ void view_save_buffer(struct sway_view *view) {
1095 view->saved_buffer_height = view->surface->current.height; 1102 view->saved_buffer_height = view->surface->current.height;
1096 } 1103 }
1097} 1104}
1105
1106bool view_is_transient_for(struct sway_view *child,
1107 struct sway_view *ancestor) {
1108 return child->impl->is_transient_for &&
1109 child->impl->is_transient_for(child, ancestor);
1110}