Skyler Grey | e27f938 | 2022-09-01 23:33:04 +0100 | [diff] [blame] | 1 | From e8a5fe93ef657399fa98bd69be71dbfbc14aa6d2 Mon Sep 17 00:00:00 2001 |
| 2 | From: Ford Smith <ford.smith@case.edu> and Skyler Grey |
| 3 | <skyler3665@gmail.com> |
| 4 | Date: Sat, 2 Apr 2022 20:21:47 -0400 |
| 5 | Subject: [PATCH] Add maximize support to sway & fix for latest version |
| 6 | |
| 7 | --- |
| 8 | include/sway/commands.h | 1 + |
| 9 | include/sway/tree/container.h | 4 + |
| 10 | include/sway/tree/view.h | 1 + |
| 11 | sway/commands.c | 2 + |
| 12 | sway/commands/maximize.c | 59 ++++++++++ |
| 13 | sway/desktop/xdg_shell.c | 8 ++ |
| 14 | sway/meson.build | 1 + |
| 15 | sway/tree/container.c | 201 ++++++++++++++++++++++++++++++++++ |
| 16 | 8 files changed, 277 insertions(+) |
| 17 | create mode 100644 sway/commands/maximize.c |
| 18 | |
| 19 | diff --git a/include/sway/commands.h b/include/sway/commands.h |
| 20 | index 2746ef28f1..7948be6823 100644 |
| 21 | --- a/include/sway/commands.h |
| 22 | +++ b/include/sway/commands.h |
| 23 | @@ -150,6 +150,7 @@ sway_cmd cmd_kill; |
| 24 | sway_cmd cmd_layout; |
| 25 | sway_cmd cmd_log_colors; |
| 26 | sway_cmd cmd_mark; |
| 27 | +sway_cmd cmd_maximize; |
| 28 | sway_cmd cmd_max_render_time; |
| 29 | sway_cmd cmd_mode; |
| 30 | sway_cmd cmd_mouse_warping; |
| 31 | diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h |
| 32 | index 751612e2c3..0a3404157b 100644 |
| 33 | --- a/include/sway/tree/container.h |
| 34 | +++ b/include/sway/tree/container.h |
| 35 | @@ -257,6 +257,10 @@ void container_end_mouse_operation(struct sway_container *container); |
| 36 | void container_set_fullscreen(struct sway_container *con, |
| 37 | enum sway_fullscreen_mode mode); |
| 38 | |
| 39 | +void container_set_maximize(struct sway_container *con, |
| 40 | + enum sway_fullscreen_mode mode); |
| 41 | + |
| 42 | + |
| 43 | /** |
| 44 | * Convenience function. |
| 45 | */ |
| 46 | diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h |
| 47 | index 95708a049c..fd186c7386 100644 |
| 48 | --- a/include/sway/tree/view.h |
| 49 | +++ b/include/sway/tree/view.h |
| 50 | @@ -43,6 +43,7 @@ struct sway_view_impl { |
| 51 | void (*set_activated)(struct sway_view *view, bool activated); |
| 52 | void (*set_tiled)(struct sway_view *view, bool tiled); |
| 53 | void (*set_fullscreen)(struct sway_view *view, bool fullscreen); |
| 54 | + void (*set_maximized)(struct sway_view *view, bool maximized); |
| 55 | void (*set_resizing)(struct sway_view *view, bool resizing); |
| 56 | bool (*wants_floating)(struct sway_view *view); |
| 57 | void (*for_each_surface)(struct sway_view *view, |
| 58 | diff --git a/sway/commands.c b/sway/commands.c |
| 59 | index 5a1fd32ef9..b33788db17 100644 |
| 60 | --- a/sway/commands.c |
| 61 | +++ b/sway/commands.c |
| 62 | @@ -74,6 +74,7 @@ static const struct cmd_handler handlers[] = { |
| 63 | { "gaps", cmd_gaps }, |
| 64 | { "hide_edge_borders", cmd_hide_edge_borders }, |
| 65 | { "input", cmd_input }, |
| 66 | + { "maximize", cmd_maximize }, |
| 67 | { "mode", cmd_mode }, |
| 68 | { "mouse_warping", cmd_mouse_warping }, |
| 69 | { "new_float", cmd_new_float }, |
| 70 | @@ -119,6 +120,7 @@ static const struct cmd_handler command_handlers[] = { |
| 71 | { "kill", cmd_kill }, |
| 72 | { "layout", cmd_layout }, |
| 73 | { "mark", cmd_mark }, |
| 74 | + { "maximize", cmd_maximize }, |
| 75 | { "max_render_time", cmd_max_render_time }, |
| 76 | { "move", cmd_move }, |
| 77 | { "nop", cmd_nop }, |
| 78 | diff --git a/sway/commands/maximize.c b/sway/commands/maximize.c |
| 79 | new file mode 100644 |
| 80 | index 0000000000..5fa29f79c1 |
| 81 | --- /dev/null |
| 82 | +++ b/sway/commands/maximize.c |
| 83 | @@ -0,0 +1,59 @@ |
| 84 | +#include <strings.h> |
| 85 | +#include "log.h" |
| 86 | +#include "sway/commands.h" |
| 87 | +#include "sway/config.h" |
| 88 | +#include "sway/tree/arrange.h" |
| 89 | +#include "sway/tree/container.h" |
| 90 | +#include "sway/tree/view.h" |
| 91 | +#include "sway/tree/workspace.h" |
| 92 | +#include "util.h" |
| 93 | + |
| 94 | +// maximize [enable|disable|toggle] [global] |
| 95 | +struct cmd_results *cmd_maximize(int argc, char **argv) { |
| 96 | + struct cmd_results *error = NULL; |
| 97 | + if ((error = checkarg(argc, "maximize", EXPECTED_AT_MOST, 2))) { |
| 98 | + return error; |
| 99 | + } |
| 100 | + if (!root->outputs->length) { |
| 101 | + return cmd_results_new(CMD_FAILURE, |
| 102 | + "Can't run this command while there's no outputs connected."); |
| 103 | + } |
| 104 | + struct sway_container *container = config->handler_context.container; |
| 105 | + |
| 106 | + if (!container) { |
| 107 | + // If the focus is not a container, do nothing successfully |
| 108 | + return cmd_results_new(CMD_SUCCESS, NULL); |
| 109 | + } else if (!container->pending.workspace) { |
| 110 | + // If in the scratchpad, operate on the highest container |
| 111 | + while (container->pending.parent) { |
| 112 | + container = container->pending.parent; |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE; |
| 117 | + bool global = false; |
| 118 | + bool enable = !is_fullscreen; |
| 119 | + |
| 120 | + if (argc >= 1) { |
| 121 | + if (strcasecmp(argv[0], "global") == 0) { |
| 122 | + global = true; |
| 123 | + } else { |
| 124 | + enable = parse_boolean(argv[0], is_fullscreen); |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + if (argc >= 2) { |
| 129 | + global = strcasecmp(argv[1], "global") == 0; |
| 130 | + } |
| 131 | + |
| 132 | + enum sway_fullscreen_mode mode = FULLSCREEN_NONE; |
| 133 | + if (enable) { |
| 134 | + mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE; |
| 135 | + } |
| 136 | + |
| 137 | + container_set_maximize(container, mode); |
| 138 | + arrange_root(); |
| 139 | + |
| 140 | + return cmd_results_new(CMD_SUCCESS, NULL); |
| 141 | +} |
| 142 | + |
| 143 | diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c |
| 144 | index 51168f4c61..f54db21930 100644 |
| 145 | --- a/sway/desktop/xdg_shell.c |
| 146 | +++ b/sway/desktop/xdg_shell.c |
| 147 | @@ -178,6 +178,13 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { |
| 148 | wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); |
| 149 | } |
| 150 | |
| 151 | +static void set_maximized(struct sway_view *view, bool maximized) { |
| 152 | + if (xdg_shell_view_from_view(view) == NULL) { |
| 153 | + return; |
| 154 | + } |
| 155 | + wlr_xdg_toplevel_set_maximized(view->wlr_xdg_surface, maximized); |
| 156 | +} |
| 157 | + |
| 158 | static void set_resizing(struct sway_view *view, bool resizing) { |
| 159 | if (xdg_shell_view_from_view(view) == NULL) { |
| 160 | return; |
| 161 | @@ -257,6 +264,7 @@ static const struct sway_view_impl view_impl = { |
| 162 | .set_activated = set_activated, |
| 163 | .set_tiled = set_tiled, |
| 164 | .set_fullscreen = set_fullscreen, |
| 165 | + .set_maximized = set_maximized, |
| 166 | .set_resizing = set_resizing, |
| 167 | .wants_floating = wants_floating, |
| 168 | .for_each_surface = for_each_surface, |
| 169 | diff --git a/sway/meson.build b/sway/meson.build |
| 170 | index 5f34ce6b01..05f302f49e 100644 |
| 171 | --- a/sway/meson.build |
| 172 | +++ b/sway/meson.build |
| 173 | @@ -69,6 +69,7 @@ sway_sources = files( |
| 174 | 'commands/inhibit_idle.c', |
| 175 | 'commands/kill.c', |
| 176 | 'commands/mark.c', |
| 177 | + 'commands/maximize.c', |
| 178 | 'commands/max_render_time.c', |
| 179 | 'commands/opacity.c', |
| 180 | 'commands/include.c', |
| 181 | diff --git a/sway/tree/container.c b/sway/tree/container.c |
| 182 | index 09766ce5cc..57f65aadb6 100644 |
| 183 | --- a/sway/tree/container.c |
| 184 | +++ b/sway/tree/container.c |
| 185 | @@ -1266,6 +1266,207 @@ void container_fullscreen_disable(struct sway_container *con) { |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | +static void set_maximized(struct sway_container *con, bool enable) { |
| 190 | + if (!con->view) { |
| 191 | + return; |
| 192 | + } |
| 193 | + if (con->view->impl->set_maximized) { |
| 194 | + con->view->impl->set_maximized(con->view, enable); |
| 195 | + if (con->view->foreign_toplevel) { |
| 196 | + wlr_foreign_toplevel_handle_v1_set_fullscreen( |
| 197 | + con->view->foreign_toplevel, enable); |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + if (!server.linux_dmabuf_v1 || !con->view->surface) { |
| 202 | + return; |
| 203 | + } |
| 204 | + if (!enable) { |
| 205 | + wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, |
| 206 | + con->view->surface, NULL); |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + if (!con->pending.workspace || !con->pending.workspace->output) { |
| 211 | + return; |
| 212 | + } |
| 213 | + |
| 214 | + struct sway_output *output = con->pending.workspace->output; |
| 215 | + struct wlr_output *wlr_output = output->wlr_output; |
| 216 | + |
| 217 | + // TODO: add wlroots helpers for all of this stuff |
| 218 | + |
| 219 | + const struct wlr_drm_format_set *renderer_formats = |
| 220 | + wlr_renderer_get_dmabuf_texture_formats(server.renderer); |
| 221 | + assert(renderer_formats); |
| 222 | + |
| 223 | + int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer); |
| 224 | + int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); |
| 225 | + if (renderer_drm_fd < 0 || backend_drm_fd < 0) { |
| 226 | + return; |
| 227 | + } |
| 228 | + |
| 229 | + dev_t render_dev, scanout_dev; |
| 230 | + if (!devid_from_fd(renderer_drm_fd, &render_dev) || |
| 231 | + !devid_from_fd(backend_drm_fd, &scanout_dev)) { |
| 232 | + return; |
| 233 | + } |
| 234 | + |
| 235 | + const struct wlr_drm_format_set *output_formats = |
| 236 | + wlr_output_get_primary_formats(output->wlr_output, |
| 237 | + WLR_BUFFER_CAP_DMABUF); |
| 238 | + if (!output_formats) { |
| 239 | + return; |
| 240 | + } |
| 241 | + |
| 242 | + struct wlr_drm_format_set scanout_formats = {0}; |
| 243 | + if (!wlr_drm_format_set_intersect(&scanout_formats, |
| 244 | + output_formats, renderer_formats)) { |
| 245 | + return; |
| 246 | + } |
| 247 | + |
| 248 | + struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { |
| 249 | + { |
| 250 | + .target_device = scanout_dev, |
| 251 | + .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, |
| 252 | + .formats = &scanout_formats, |
| 253 | + }, |
| 254 | + { |
| 255 | + .target_device = render_dev, |
| 256 | + .formats = renderer_formats, |
| 257 | + }, |
| 258 | + }; |
| 259 | + |
| 260 | + const struct wlr_linux_dmabuf_feedback_v1 feedback = { |
| 261 | + .main_device = render_dev, |
| 262 | + .tranches = tranches, |
| 263 | + .tranches_len = sizeof(tranches) / sizeof(tranches[0]), |
| 264 | + }; |
| 265 | + wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, |
| 266 | + con->view->surface, &feedback); |
| 267 | + |
| 268 | + wlr_drm_format_set_finish(&scanout_formats); |
| 269 | +} |
| 270 | + |
| 271 | +static void container_maximize_workspace(struct sway_container *con) { |
| 272 | + if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE, |
| 273 | + "Expected a non-fullscreen container")) { |
| 274 | + return; |
| 275 | + } |
| 276 | + set_maximized(con, true); |
| 277 | + con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE; |
| 278 | + |
| 279 | + con->saved_x = con->pending.x; |
| 280 | + con->saved_y = con->pending.y; |
| 281 | + con->saved_width = con->pending.width; |
| 282 | + con->saved_height = con->pending.height; |
| 283 | + |
| 284 | + if (con->pending.workspace) { |
| 285 | + con->pending.workspace->fullscreen = con; |
| 286 | + struct sway_seat *seat; |
| 287 | + struct sway_workspace *focus_ws; |
| 288 | + wl_list_for_each(seat, &server.input->seats, link) { |
| 289 | + focus_ws = seat_get_focused_workspace(seat); |
| 290 | + if (focus_ws == con->pending.workspace) { |
| 291 | + seat_set_focus_container(seat, con); |
| 292 | + } else { |
| 293 | + struct sway_node *focus = |
| 294 | + seat_get_focus_inactive(seat, &root->node); |
| 295 | + seat_set_raw_focus(seat, &con->node); |
| 296 | + seat_set_raw_focus(seat, focus); |
| 297 | + } |
| 298 | + } |
| 299 | + } |
| 300 | + |
| 301 | + container_end_mouse_operation(con); |
| 302 | + ipc_event_window(con, "fullscreen_mode"); |
| 303 | +} |
| 304 | + |
| 305 | +void container_maximize_disable(struct sway_container *con) { |
| 306 | + if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE, |
| 307 | + "Expected a fullscreen container")) { |
| 308 | + return; |
| 309 | + } |
| 310 | + set_maximized(con, false); |
| 311 | + |
| 312 | + if (container_is_floating(con)) { |
| 313 | + con->pending.x = con->saved_x; |
| 314 | + con->pending.y = con->saved_y; |
| 315 | + con->pending.width = con->saved_width; |
| 316 | + con->pending.height = con->saved_height; |
| 317 | + } |
| 318 | + |
| 319 | + if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
| 320 | + if (con->pending.workspace) { |
| 321 | + con->pending.workspace->fullscreen = NULL; |
| 322 | + if (container_is_floating(con)) { |
| 323 | + struct sway_output *output = |
| 324 | + container_floating_find_output(con); |
| 325 | + if (con->pending.workspace->output != output) { |
| 326 | + container_floating_move_to_center(con); |
| 327 | + } |
| 328 | + } |
| 329 | + } |
| 330 | + } else { |
| 331 | + root->fullscreen_global = NULL; |
| 332 | + } |
| 333 | + |
| 334 | + // If the container was mapped as fullscreen and set as floating by |
| 335 | + // criteria, it needs to be reinitialized as floating to get the proper |
| 336 | + // size and location |
| 337 | + if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) { |
| 338 | + container_floating_resize_and_center(con); |
| 339 | + } |
| 340 | + |
| 341 | + con->pending.fullscreen_mode = FULLSCREEN_NONE; |
| 342 | + container_end_mouse_operation(con); |
| 343 | + ipc_event_window(con, "fullscreen_mode"); |
| 344 | + |
| 345 | + if (con->scratchpad) { |
| 346 | + struct sway_seat *seat; |
| 347 | + wl_list_for_each(seat, &server.input->seats, link) { |
| 348 | + struct sway_container *focus = seat_get_focused_container(seat); |
| 349 | + if (focus == con || container_has_ancestor(focus, con)) { |
| 350 | + seat_set_focus(seat, |
| 351 | + seat_get_focus_inactive(seat, &root->node)); |
| 352 | + } |
| 353 | + } |
| 354 | + } |
| 355 | +} |
| 356 | + |
| 357 | +void container_set_maximize(struct sway_container *con, |
| 358 | + enum sway_fullscreen_mode mode) { |
| 359 | + if (con->pending.fullscreen_mode == mode) { |
| 360 | + return; |
| 361 | + } |
| 362 | + |
| 363 | + switch (mode) { |
| 364 | + case FULLSCREEN_NONE: |
| 365 | + container_maximize_disable(con); |
| 366 | + break; |
| 367 | + case FULLSCREEN_WORKSPACE: |
| 368 | + if (root->fullscreen_global) { |
| 369 | + container_maximize_disable(root->fullscreen_global); |
| 370 | + } |
| 371 | + if (con->pending.workspace && con->pending.workspace->fullscreen) { |
| 372 | + container_maximize_disable(con->pending.workspace->fullscreen); |
| 373 | + } |
| 374 | + container_maximize_workspace(con); |
| 375 | + break; |
| 376 | + case FULLSCREEN_GLOBAL: |
| 377 | + //TODO: |
| 378 | + assert(false); |
| 379 | + // if (root->fullscreen_global) { |
| 380 | + // container_maximize_disable(root->fullscreen_global); |
| 381 | + // } |
| 382 | + // if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { |
| 383 | + // container_maximize_disable(con); |
| 384 | + // } |
| 385 | + // container_fullscreen_global(con); |
| 386 | + // break; |
| 387 | + } |
| 388 | +} |
| 389 | + |
| 390 | void container_set_fullscreen(struct sway_container *con, |
| 391 | enum sway_fullscreen_mode mode) { |
| 392 | if (con->pending.fullscreen_mode == mode) { |