diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index c3389fe8..dd16a175 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -1,6 +1,7 @@ #ifndef _SWAY_IPC_SERVER_H #define _SWAY_IPC_SERVER_H #include +#include "sway/config.h" #include "sway/tree/container.h" #include "ipc.h" diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 2a8b8aba..22bd7240 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -72,9 +72,13 @@ struct sway_container { // For C_OUTPUT, this is the output position in layout coordinates // For other types, this is the position in output-local coordinates double x, y; + double saved_x, saved_y; // does not include borders or gaps. double width, height; + // For C_WORKSPACE only + struct sway_view *fullscreen; + list_t *children; struct sway_container *parent; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index b51c54b5..73d5f6c7 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -28,6 +28,7 @@ struct sway_view_impl { void (*configure)(struct sway_view *view, double ox, double oy, int width, int height); void (*set_activated)(struct sway_view *view, bool activated); + void (*set_fullscreen)(struct sway_view *view, bool fullscreen); void (*for_each_surface)(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data); void (*close)(struct sway_view *view); @@ -41,6 +42,8 @@ struct sway_view { struct sway_container *swayc; // NULL for unmanaged views struct wlr_surface *surface; // NULL for unmapped views int width, height; + int saved_width, saved_height; + bool is_fullscreen; union { struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; @@ -63,6 +66,7 @@ struct sway_xdg_shell_v6_view { struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; + struct wl_listener request_fullscreen; struct wl_listener new_popup; struct wl_listener map; struct wl_listener unmap; @@ -79,6 +83,7 @@ struct sway_xwayland_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_configure; + struct wl_listener request_fullscreen; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -93,6 +98,7 @@ struct sway_xwayland_unmanaged { int lx, ly; struct wl_listener request_configure; + struct wl_listener request_fullscreen; struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; @@ -106,6 +112,7 @@ struct sway_wl_shell_view { struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; + struct wl_listener request_fullscreen; struct wl_listener destroy; int pending_width, pending_height; @@ -155,6 +162,8 @@ void view_configure(struct sway_view *view, double ox, double oy, int width, void view_set_activated(struct sway_view *view, bool activated); +void view_set_fullscreen(struct sway_view *view, bool fullscreen); + void view_close(struct sway_view *view); void view_damage(struct sway_view *view, bool whole); diff --git a/sway/commands.c b/sway/commands.c index 99f42524..8ddc033b 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -99,6 +99,7 @@ static struct cmd_handler handlers[] = { { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, { "focus_follows_mouse", cmd_focus_follows_mouse }, + { "fullscreen", cmd_fullscreen }, { "include", cmd_include }, { "input", cmd_input }, { "mode", cmd_mode }, diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c new file mode 100644 index 00000000..3e256282 --- /dev/null +++ b/sway/commands/fullscreen.c @@ -0,0 +1,40 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +// fullscreen toggle|enable|disable +struct cmd_results *cmd_fullscreen(int argc, char **argv) { + struct cmd_results *error = NULL; + if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file."); + if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running."); + if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct sway_container *container = + config->handler_context.current_container; + if (container->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "fullscreen", + "Only views can fullscreen"); + } + struct sway_view *view = container->sway_view; + bool wants_fullscreen; + + if (strcmp(argv[0], "enable") == 0) { + wants_fullscreen = true; + } else if (strcmp(argv[0], "disable") == 0) { + wants_fullscreen = false; + } else if (strcmp(argv[0], "toggle") == 0) { + wants_fullscreen = !view->is_fullscreen; + } else { + return cmd_results_new(CMD_INVALID, "fullscreen", + "Expected 'fullscreen '"); + } + + view_set_fullscreen(view, wants_fullscreen); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1b3143d0..b86f20e8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -273,17 +273,25 @@ static void render_output(struct sway_output *output, struct timespec *when, float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; wlr_renderer_clear(renderer, clear_color); - render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - struct sway_container *workspace = output_get_active_workspace(output); - render_container(output, workspace); - render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged); + if (workspace->fullscreen) { + wlr_output_set_fullscreen_surface(wlr_output, + workspace->fullscreen->surface); + } else { + wlr_output_set_fullscreen_surface(wlr_output, NULL); + render_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - // TODO: consider revising this when fullscreen windows are supported - render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + render_container(output, workspace); + + render_unmanaged(output, &root_container.sway_root->xwayland_unmanaged); + + render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer(output, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + } renderer_end: if (root_container.sway_root->debug_tree) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index e4703040..133b60c3 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -118,6 +118,14 @@ static void set_activated(struct sway_view *view, bool activated) { } } +static void set_fullscreen(struct sway_view *view, bool fullscreen) { + if (xdg_shell_v6_view_from_view(view) == NULL) { + return; + } + struct wlr_xdg_surface_v6 *surface = view->wlr_xdg_surface_v6; + wlr_xdg_toplevel_v6_set_fullscreen(surface, fullscreen); +} + static void for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (xdg_shell_v6_view_from_view(view) == NULL) { @@ -146,6 +154,7 @@ static void destroy(struct sway_view *view) { wl_list_remove(&xdg_shell_v6_view->destroy.link); wl_list_remove(&xdg_shell_v6_view->map.link); wl_list_remove(&xdg_shell_v6_view->unmap.link); + wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); free(xdg_shell_v6_view); } @@ -153,6 +162,7 @@ static const struct sway_view_impl view_impl = { .get_prop = get_prop, .configure = configure, .set_activated = set_activated, + .set_fullscreen = set_fullscreen, .for_each_surface = for_each_surface, .close = _close, .destroy = destroy, @@ -210,6 +220,18 @@ static void handle_destroy(struct wl_listener *listener, void *data) { view_destroy(&xdg_shell_v6_view->view); } +static void handle_request_fullscreen(struct wl_listener *listener, void *data) { + struct sway_xdg_shell_v6_view *xdg_shell_v6_view = + wl_container_of(listener, xdg_shell_v6_view, request_fullscreen); + struct wlr_xdg_toplevel_v6_set_fullscreen_event *e = data; + + if (xdg_shell_v6_view->view.wlr_xdg_surface_v6->role != WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { + return; + } + + view_set_fullscreen(&xdg_shell_v6_view->view, e->fullscreen); +} + void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, xdg_shell_v6_surface); @@ -246,4 +268,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { xdg_shell_v6_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_v6_view->destroy); + + xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, + &xdg_shell_v6_view->request_fullscreen); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 69166af0..716d8882 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -25,6 +25,15 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, ev->width, ev->height); } +static void unmanaged_handle_request_fullscreen(struct wl_listener *listener, + void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_fullscreen); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + view_set_fullscreen(view, xsurface->fullscreen); +} + static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, commit); @@ -106,6 +115,9 @@ static struct sway_xwayland_unmanaged *create_unmanaged( wl_signal_add(&xsurface->events.request_configure, &surface->request_configure); surface->request_configure.notify = unmanaged_handle_request_configure; + wl_signal_add(&xsurface->events.request_fullscreen, + &surface->request_fullscreen); + surface->request_fullscreen.notify = unmanaged_handle_request_fullscreen; wl_signal_add(&xsurface->events.map, &surface->map); surface->map.notify = unmanaged_handle_map; wl_signal_add(&xsurface->events.unmap, &surface->unmap); @@ -179,6 +191,14 @@ static void set_activated(struct sway_view *view, bool activated) { wlr_xwayland_surface_activate(surface, activated); } +static void set_fullscreen(struct sway_view *view, bool fullscreen) { + if (xwayland_view_from_view(view) == NULL) { + return; + } + struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; + wlr_xwayland_surface_set_fullscreen(surface, fullscreen); +} + static void _close(struct sway_view *view) { if (xwayland_view_from_view(view) == NULL) { return; @@ -193,6 +213,7 @@ static void destroy(struct sway_view *view) { } wl_list_remove(&xwayland_view->destroy.link); wl_list_remove(&xwayland_view->request_configure.link); + wl_list_remove(&xwayland_view->request_fullscreen.link); wl_list_remove(&xwayland_view->map.link); wl_list_remove(&xwayland_view->unmap.link); free(xwayland_view); @@ -202,6 +223,7 @@ static const struct sway_view_impl view_impl = { .get_prop = get_prop, .configure = configure, .set_activated = set_activated, + .set_fullscreen = set_fullscreen, .close = _close, .destroy = destroy, }; @@ -263,6 +285,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { ev->width, ev->height); } +static void handle_request_fullscreen(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_fullscreen); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + view_set_fullscreen(view, xsurface->fullscreen); +} + void handle_xwayland_surface(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, xwayland_surface); @@ -298,6 +328,10 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->request_configure); xwayland_view->request_configure.notify = handle_request_configure; + wl_signal_add(&xsurface->events.request_fullscreen, + &xwayland_view->request_fullscreen); + xwayland_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); xwayland_view->unmap.notify = handle_unmap; diff --git a/sway/input/seat.c b/sway/input/seat.c index 09927a1a..f60c43b5 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -448,6 +448,20 @@ void seat_set_focus_warp(struct sway_seat *seat, return; } + struct sway_container *last_workspace = last_focus; + if (last_workspace && last_workspace->type != C_WORKSPACE) { + last_workspace = container_parent(last_workspace, C_WORKSPACE); + } + struct sway_container *new_workspace = container; + if (new_workspace && new_workspace->type != C_WORKSPACE) { + new_workspace = container_parent(new_workspace, C_WORKSPACE); + } + + if (last_workspace == new_workspace && last_workspace->fullscreen + && !container->sway_view->is_fullscreen) { + return; + } + struct sway_container *last_output = last_focus; if (last_output && last_output->type != C_OUTPUT) { last_output = container_parent(last_output, C_OUTPUT); diff --git a/sway/meson.build b/sway/meson.build index 9e55e335..0bbb8da1 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -34,6 +34,7 @@ sway_sources = files( 'commands/exec_always.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', + 'commands/fullscreen.c', 'commands/kill.c', 'commands/opacity.c', 'commands/include.c', diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 0b637822..ae6db454 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -137,6 +137,21 @@ void container_move_to(struct sway_container *container, || container_has_anscestor(container, destination)) { return; } + + if (container->sway_view->is_fullscreen) { + struct sway_container *old_workspace = container; + if (old_workspace->type != C_WORKSPACE) { + old_workspace = container_parent(old_workspace, C_WORKSPACE); + } + struct sway_container *new_workspace = destination; + if (new_workspace->type != C_WORKSPACE) { + new_workspace = container_parent(new_workspace, C_WORKSPACE); + } + if (old_workspace != new_workspace) { + view_set_fullscreen(container->sway_view, false); + } + } + struct sway_container *old_parent = container_remove_child(container); container->width = container->height = 0; struct sway_container *new_parent; @@ -557,6 +572,9 @@ void arrange_windows(struct sway_container *container, return; case C_WORKSPACE: { + if (container->fullscreen) { + return; + } struct sway_container *output = container_parent(container, C_OUTPUT); struct wlr_box *area = &output->sway_output->usable_area; diff --git a/sway/tree/view.c b/sway/tree/view.c index 99b44720..b958233b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -2,6 +2,7 @@ #include #include #include "log.h" +#include "sway/ipc-server.h" #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/layout.h" @@ -73,7 +74,46 @@ void view_set_activated(struct sway_view *view, bool activated) { } } +void view_set_fullscreen(struct sway_view *view, bool fullscreen) { + if (view->is_fullscreen == fullscreen) { + return; + } + + struct sway_container *container = container_parent(view->swayc, C_OUTPUT); + struct sway_output *output = container->sway_output; + struct sway_container *workspace = container_parent(view->swayc, C_WORKSPACE); + + if (view->impl->set_fullscreen) { + view->impl->set_fullscreen(view, fullscreen); + } + + if (fullscreen) { + view->swayc->saved_x = view->swayc->x; + view->swayc->saved_y = view->swayc->y; + view->saved_width = view->width; + view->saved_height = view->height; + view_configure(view, 0, 0, output->wlr_output->width, output->wlr_output->height); + workspace->fullscreen = view; + } else { + view_configure(view, view->swayc->saved_x, view->swayc->saved_y, + view->saved_width, view->saved_height); + workspace->fullscreen = NULL; + } + + view->is_fullscreen = fullscreen; + output_damage_whole(output); + + arrange_windows(workspace, -1, -1); + + ipc_event_window(view->swayc, "fullscreen_mode"); +} + void view_close(struct sway_view *view) { + if (view->is_fullscreen) { + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + ws->fullscreen = NULL; + } + if (view->impl->close) { view->impl->close(view); }