Allow sway to maximize windows without fullscreen
- Add a patch that enables `swaymsg maximize`, maximizing a window (i.e. chrome)
without telling it that it's in fullscreen mode (similar to other window
managers' maximize functionality)
- Replace sway with sway-unwrapped: we need it to apply patches
diff --git a/modules/sway.nix b/modules/sway.nix
index a5415df..da837c9 100644
--- a/modules/sway.nix
+++ b/modules/sway.nix
@@ -1,12 +1,14 @@
{
pkgs,
lib,
+ home,
...
}: {
home = {
wayland.windowManager.sway = {
enable = true;
wrapperFeatures.gtk = true;
+ package = pkgs.sway-unwrapped;
config = rec {
assigns = {};
@@ -61,7 +63,7 @@
};
modifier = "Mod4";
output = {
- HDMI-A-2 = {
+ HDMI-A-1 = {
resolution = "3840x2160";
bg = "#FFD0F9 solid_color";
position = "0,0";
diff --git a/patches/sway-unwrapped/maximize.patch b/patches/sway-unwrapped/maximize.patch
new file mode 100644
index 0000000..dcab9bf
--- /dev/null
+++ b/patches/sway-unwrapped/maximize.patch
@@ -0,0 +1,392 @@
+From e8a5fe93ef657399fa98bd69be71dbfbc14aa6d2 Mon Sep 17 00:00:00 2001
+From: Ford Smith <ford.smith@case.edu> and Skyler Grey
+<skyler3665@gmail.com>
+Date: Sat, 2 Apr 2022 20:21:47 -0400
+Subject: [PATCH] Add maximize support to sway & fix for latest version
+
+---
+ include/sway/commands.h | 1 +
+ include/sway/tree/container.h | 4 +
+ include/sway/tree/view.h | 1 +
+ sway/commands.c | 2 +
+ sway/commands/maximize.c | 59 ++++++++++
+ sway/desktop/xdg_shell.c | 8 ++
+ sway/meson.build | 1 +
+ sway/tree/container.c | 201 ++++++++++++++++++++++++++++++++++
+ 8 files changed, 277 insertions(+)
+ create mode 100644 sway/commands/maximize.c
+
+diff --git a/include/sway/commands.h b/include/sway/commands.h
+index 2746ef28f1..7948be6823 100644
+--- a/include/sway/commands.h
++++ b/include/sway/commands.h
+@@ -150,6 +150,7 @@ sway_cmd cmd_kill;
+ sway_cmd cmd_layout;
+ sway_cmd cmd_log_colors;
+ sway_cmd cmd_mark;
++sway_cmd cmd_maximize;
+ sway_cmd cmd_max_render_time;
+ sway_cmd cmd_mode;
+ sway_cmd cmd_mouse_warping;
+diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
+index 751612e2c3..0a3404157b 100644
+--- a/include/sway/tree/container.h
++++ b/include/sway/tree/container.h
+@@ -257,6 +257,10 @@ void container_end_mouse_operation(struct sway_container *container);
+ void container_set_fullscreen(struct sway_container *con,
+ enum sway_fullscreen_mode mode);
+
++void container_set_maximize(struct sway_container *con,
++ enum sway_fullscreen_mode mode);
++
++
+ /**
+ * Convenience function.
+ */
+diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
+index 95708a049c..fd186c7386 100644
+--- a/include/sway/tree/view.h
++++ b/include/sway/tree/view.h
+@@ -43,6 +43,7 @@ struct sway_view_impl {
+ void (*set_activated)(struct sway_view *view, bool activated);
+ void (*set_tiled)(struct sway_view *view, bool tiled);
+ void (*set_fullscreen)(struct sway_view *view, bool fullscreen);
++ void (*set_maximized)(struct sway_view *view, bool maximized);
+ void (*set_resizing)(struct sway_view *view, bool resizing);
+ bool (*wants_floating)(struct sway_view *view);
+ void (*for_each_surface)(struct sway_view *view,
+diff --git a/sway/commands.c b/sway/commands.c
+index 5a1fd32ef9..b33788db17 100644
+--- a/sway/commands.c
++++ b/sway/commands.c
+@@ -74,6 +74,7 @@ static const struct cmd_handler handlers[] = {
+ { "gaps", cmd_gaps },
+ { "hide_edge_borders", cmd_hide_edge_borders },
+ { "input", cmd_input },
++ { "maximize", cmd_maximize },
+ { "mode", cmd_mode },
+ { "mouse_warping", cmd_mouse_warping },
+ { "new_float", cmd_new_float },
+@@ -119,6 +120,7 @@ static const struct cmd_handler command_handlers[] = {
+ { "kill", cmd_kill },
+ { "layout", cmd_layout },
+ { "mark", cmd_mark },
++ { "maximize", cmd_maximize },
+ { "max_render_time", cmd_max_render_time },
+ { "move", cmd_move },
+ { "nop", cmd_nop },
+diff --git a/sway/commands/maximize.c b/sway/commands/maximize.c
+new file mode 100644
+index 0000000000..5fa29f79c1
+--- /dev/null
++++ b/sway/commands/maximize.c
+@@ -0,0 +1,59 @@
++#include <strings.h>
++#include "log.h"
++#include "sway/commands.h"
++#include "sway/config.h"
++#include "sway/tree/arrange.h"
++#include "sway/tree/container.h"
++#include "sway/tree/view.h"
++#include "sway/tree/workspace.h"
++#include "util.h"
++
++// maximize [enable|disable|toggle] [global]
++struct cmd_results *cmd_maximize(int argc, char **argv) {
++ struct cmd_results *error = NULL;
++ if ((error = checkarg(argc, "maximize", EXPECTED_AT_MOST, 2))) {
++ return error;
++ }
++ if (!root->outputs->length) {
++ return cmd_results_new(CMD_FAILURE,
++ "Can't run this command while there's no outputs connected.");
++ }
++ struct sway_container *container = config->handler_context.container;
++
++ if (!container) {
++ // If the focus is not a container, do nothing successfully
++ return cmd_results_new(CMD_SUCCESS, NULL);
++ } else if (!container->pending.workspace) {
++ // If in the scratchpad, operate on the highest container
++ while (container->pending.parent) {
++ container = container->pending.parent;
++ }
++ }
++
++ bool is_fullscreen = container->pending.fullscreen_mode != FULLSCREEN_NONE;
++ bool global = false;
++ bool enable = !is_fullscreen;
++
++ if (argc >= 1) {
++ if (strcasecmp(argv[0], "global") == 0) {
++ global = true;
++ } else {
++ enable = parse_boolean(argv[0], is_fullscreen);
++ }
++ }
++
++ if (argc >= 2) {
++ global = strcasecmp(argv[1], "global") == 0;
++ }
++
++ enum sway_fullscreen_mode mode = FULLSCREEN_NONE;
++ if (enable) {
++ mode = global ? FULLSCREEN_GLOBAL : FULLSCREEN_WORKSPACE;
++ }
++
++ container_set_maximize(container, mode);
++ arrange_root();
++
++ return cmd_results_new(CMD_SUCCESS, NULL);
++}
++
+diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
+index 51168f4c61..f54db21930 100644
+--- a/sway/desktop/xdg_shell.c
++++ b/sway/desktop/xdg_shell.c
+@@ -178,6 +178,13 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) {
+ wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
+ }
+
++static void set_maximized(struct sway_view *view, bool maximized) {
++ if (xdg_shell_view_from_view(view) == NULL) {
++ return;
++ }
++ wlr_xdg_toplevel_set_maximized(view->wlr_xdg_surface, maximized);
++}
++
+ static void set_resizing(struct sway_view *view, bool resizing) {
+ if (xdg_shell_view_from_view(view) == NULL) {
+ return;
+@@ -257,6 +264,7 @@ static const struct sway_view_impl view_impl = {
+ .set_activated = set_activated,
+ .set_tiled = set_tiled,
+ .set_fullscreen = set_fullscreen,
++ .set_maximized = set_maximized,
+ .set_resizing = set_resizing,
+ .wants_floating = wants_floating,
+ .for_each_surface = for_each_surface,
+diff --git a/sway/meson.build b/sway/meson.build
+index 5f34ce6b01..05f302f49e 100644
+--- a/sway/meson.build
++++ b/sway/meson.build
+@@ -69,6 +69,7 @@ sway_sources = files(
+ 'commands/inhibit_idle.c',
+ 'commands/kill.c',
+ 'commands/mark.c',
++ 'commands/maximize.c',
+ 'commands/max_render_time.c',
+ 'commands/opacity.c',
+ 'commands/include.c',
+diff --git a/sway/tree/container.c b/sway/tree/container.c
+index 09766ce5cc..57f65aadb6 100644
+--- a/sway/tree/container.c
++++ b/sway/tree/container.c
+@@ -1266,6 +1266,207 @@ void container_fullscreen_disable(struct sway_container *con) {
+ }
+ }
+
++static void set_maximized(struct sway_container *con, bool enable) {
++ if (!con->view) {
++ return;
++ }
++ if (con->view->impl->set_maximized) {
++ con->view->impl->set_maximized(con->view, enable);
++ if (con->view->foreign_toplevel) {
++ wlr_foreign_toplevel_handle_v1_set_fullscreen(
++ con->view->foreign_toplevel, enable);
++ }
++ }
++
++ if (!server.linux_dmabuf_v1 || !con->view->surface) {
++ return;
++ }
++ if (!enable) {
++ wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
++ con->view->surface, NULL);
++ return;
++ }
++
++ if (!con->pending.workspace || !con->pending.workspace->output) {
++ return;
++ }
++
++ struct sway_output *output = con->pending.workspace->output;
++ struct wlr_output *wlr_output = output->wlr_output;
++
++ // TODO: add wlroots helpers for all of this stuff
++
++ const struct wlr_drm_format_set *renderer_formats =
++ wlr_renderer_get_dmabuf_texture_formats(server.renderer);
++ assert(renderer_formats);
++
++ int renderer_drm_fd = wlr_renderer_get_drm_fd(server.renderer);
++ int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend);
++ if (renderer_drm_fd < 0 || backend_drm_fd < 0) {
++ return;
++ }
++
++ dev_t render_dev, scanout_dev;
++ if (!devid_from_fd(renderer_drm_fd, &render_dev) ||
++ !devid_from_fd(backend_drm_fd, &scanout_dev)) {
++ return;
++ }
++
++ const struct wlr_drm_format_set *output_formats =
++ wlr_output_get_primary_formats(output->wlr_output,
++ WLR_BUFFER_CAP_DMABUF);
++ if (!output_formats) {
++ return;
++ }
++
++ struct wlr_drm_format_set scanout_formats = {0};
++ if (!wlr_drm_format_set_intersect(&scanout_formats,
++ output_formats, renderer_formats)) {
++ return;
++ }
++
++ struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = {
++ {
++ .target_device = scanout_dev,
++ .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
++ .formats = &scanout_formats,
++ },
++ {
++ .target_device = render_dev,
++ .formats = renderer_formats,
++ },
++ };
++
++ const struct wlr_linux_dmabuf_feedback_v1 feedback = {
++ .main_device = render_dev,
++ .tranches = tranches,
++ .tranches_len = sizeof(tranches) / sizeof(tranches[0]),
++ };
++ wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1,
++ con->view->surface, &feedback);
++
++ wlr_drm_format_set_finish(&scanout_formats);
++}
++
++static void container_maximize_workspace(struct sway_container *con) {
++ if (!sway_assert(con->pending.fullscreen_mode == FULLSCREEN_NONE,
++ "Expected a non-fullscreen container")) {
++ return;
++ }
++ set_maximized(con, true);
++ con->pending.fullscreen_mode = FULLSCREEN_WORKSPACE;
++
++ con->saved_x = con->pending.x;
++ con->saved_y = con->pending.y;
++ con->saved_width = con->pending.width;
++ con->saved_height = con->pending.height;
++
++ if (con->pending.workspace) {
++ con->pending.workspace->fullscreen = con;
++ struct sway_seat *seat;
++ struct sway_workspace *focus_ws;
++ wl_list_for_each(seat, &server.input->seats, link) {
++ focus_ws = seat_get_focused_workspace(seat);
++ if (focus_ws == con->pending.workspace) {
++ seat_set_focus_container(seat, con);
++ } else {
++ struct sway_node *focus =
++ seat_get_focus_inactive(seat, &root->node);
++ seat_set_raw_focus(seat, &con->node);
++ seat_set_raw_focus(seat, focus);
++ }
++ }
++ }
++
++ container_end_mouse_operation(con);
++ ipc_event_window(con, "fullscreen_mode");
++}
++
++void container_maximize_disable(struct sway_container *con) {
++ if (!sway_assert(con->pending.fullscreen_mode != FULLSCREEN_NONE,
++ "Expected a fullscreen container")) {
++ return;
++ }
++ set_maximized(con, false);
++
++ if (container_is_floating(con)) {
++ con->pending.x = con->saved_x;
++ con->pending.y = con->saved_y;
++ con->pending.width = con->saved_width;
++ con->pending.height = con->saved_height;
++ }
++
++ if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
++ if (con->pending.workspace) {
++ con->pending.workspace->fullscreen = NULL;
++ if (container_is_floating(con)) {
++ struct sway_output *output =
++ container_floating_find_output(con);
++ if (con->pending.workspace->output != output) {
++ container_floating_move_to_center(con);
++ }
++ }
++ }
++ } else {
++ root->fullscreen_global = NULL;
++ }
++
++ // If the container was mapped as fullscreen and set as floating by
++ // criteria, it needs to be reinitialized as floating to get the proper
++ // size and location
++ if (container_is_floating(con) && (con->pending.width == 0 || con->pending.height == 0)) {
++ container_floating_resize_and_center(con);
++ }
++
++ con->pending.fullscreen_mode = FULLSCREEN_NONE;
++ container_end_mouse_operation(con);
++ ipc_event_window(con, "fullscreen_mode");
++
++ if (con->scratchpad) {
++ struct sway_seat *seat;
++ wl_list_for_each(seat, &server.input->seats, link) {
++ struct sway_container *focus = seat_get_focused_container(seat);
++ if (focus == con || container_has_ancestor(focus, con)) {
++ seat_set_focus(seat,
++ seat_get_focus_inactive(seat, &root->node));
++ }
++ }
++ }
++}
++
++void container_set_maximize(struct sway_container *con,
++ enum sway_fullscreen_mode mode) {
++ if (con->pending.fullscreen_mode == mode) {
++ return;
++ }
++
++ switch (mode) {
++ case FULLSCREEN_NONE:
++ container_maximize_disable(con);
++ break;
++ case FULLSCREEN_WORKSPACE:
++ if (root->fullscreen_global) {
++ container_maximize_disable(root->fullscreen_global);
++ }
++ if (con->pending.workspace && con->pending.workspace->fullscreen) {
++ container_maximize_disable(con->pending.workspace->fullscreen);
++ }
++ container_maximize_workspace(con);
++ break;
++ case FULLSCREEN_GLOBAL:
++ //TODO:
++ assert(false);
++ // if (root->fullscreen_global) {
++ // container_maximize_disable(root->fullscreen_global);
++ // }
++ // if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) {
++ // container_maximize_disable(con);
++ // }
++ // container_fullscreen_global(con);
++ // break;
++ }
++}
++
+ void container_set_fullscreen(struct sway_container *con,
+ enum sway_fullscreen_mode mode) {
+ if (con->pending.fullscreen_mode == mode) {