commit 9a1c411abd8261c121dcd50dfe54132718768084
parent b881c2e84c4be3c7b996f85200cfe391a7979267
Author: Ricardo Steijn <61013287+RicArch97@users.noreply.github.com>
Date: Mon, 5 Aug 2024 02:13:49 +0200
Add support for tearing-control-v1
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3871
Adds option to allow tearing per output, as well as an option to force
enable or disable tearing for a specific application using a window
rule. Only works with fullscreen applications.
Diffstat:
20 files changed, 243 insertions(+), 3 deletions(-)
diff --git a/include/sway/commands.h b/include/sway/commands.h
@@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
sway_cmd cmd_exec_validate;
sway_cmd cmd_exec_process;
+sway_cmd cmd_allow_tearing;
sway_cmd cmd_assign;
sway_cmd cmd_bar;
sway_cmd cmd_bindcode;
@@ -283,6 +284,7 @@ sway_cmd input_cmd_xkb_switch_layout;
sway_cmd input_cmd_xkb_variant;
sway_cmd output_cmd_adaptive_sync;
+sway_cmd output_cmd_allow_tearing;
sway_cmd output_cmd_background;
sway_cmd output_cmd_color_profile;
sway_cmd output_cmd_disable;
diff --git a/include/sway/config.h b/include/sway/config.h
@@ -289,6 +289,7 @@ struct output_config {
enum render_bit_depth render_bit_depth;
bool set_color_transform;
struct wlr_color_transform *color_transform;
+ int allow_tearing;
char *background;
char *background_option;
diff --git a/include/sway/output.h b/include/sway/output.h
@@ -73,6 +73,7 @@ struct sway_output {
int max_render_time; // In milliseconds
struct wl_event_source *repaint_timer;
bool gamma_lut_changed;
+ bool allow_tearing;
};
struct sway_output_non_desktop {
diff --git a/include/sway/server.h b/include/sway/server.h
@@ -115,6 +115,10 @@ struct sway_server {
struct wl_listener xdg_activation_v1_new_token;
struct wl_listener request_set_cursor_shape;
+
+ struct wlr_tearing_control_manager_v1 *tearing_control_v1;
+ struct wl_listener tearing_control_new_object;
+ struct wl_list tearing_controllers; // sway_tearing_controller::link
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
@@ -182,4 +186,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
void set_rr_scheduling(void);
+void handle_new_tearing_hint(struct wl_listener *listener, void *data);
+
#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
@@ -4,6 +4,7 @@
#include <wlr/config.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_tearing_control_v1.h>
#include "sway/config.h"
#if WLR_HAS_XWAYLAND
#include <wlr/xwayland.h>
@@ -34,6 +35,12 @@ enum sway_view_prop {
#endif
};
+enum sway_view_tearing_mode {
+ TEARING_OVERRIDE_FALSE,
+ TEARING_OVERRIDE_TRUE,
+ TEARING_WINDOW_HINT,
+};
+
struct sway_view_impl {
void (*get_constraints)(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height);
@@ -111,6 +118,9 @@ struct sway_view {
int max_render_time; // In milliseconds
enum seat_config_shortcuts_inhibit shortcuts_inhibit;
+
+ enum sway_view_tearing_mode tearing_mode;
+ enum wp_tearing_control_v1_presentation_hint tearing_hint;
};
struct sway_xdg_shell_view {
@@ -335,4 +345,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
void view_send_frame_done(struct sway_view *view);
+bool view_can_tear(struct sway_view *view);
+
#endif
diff --git a/protocols/meson.build b/protocols/meson.build
@@ -14,6 +14,7 @@ protocols = [
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
wl_protocol_dir / 'staging/content-type/content-type-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
+ wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'idle.xml',
'wlr-output-power-management-unstable-v1.xml',
diff --git a/sway/commands.c b/sway/commands.c
@@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = {
/* Runtime-only commands. Keep alphabetized */
static const struct cmd_handler command_handlers[] = {
+ { "allow_tearing", cmd_allow_tearing },
{ "border", cmd_border },
{ "create_output", cmd_create_output },
{ "exit", cmd_exit },
diff --git a/sway/commands/allow_tearing.c b/sway/commands/allow_tearing.c
@@ -0,0 +1,24 @@
+#include <sway/commands.h>
+#include "sway/config.h"
+#include "sway/tree/view.h"
+#include "util.h"
+
+struct cmd_results *cmd_allow_tearing(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) {
+ return error;
+ }
+
+ struct sway_container *container = config->handler_context.container;
+ if (!container || !container->view) {
+ return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views");
+ }
+
+ bool wants_tearing = parse_boolean(argv[0], true);
+
+ struct sway_view *view = container->view;
+ view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE :
+ TEARING_OVERRIDE_FALSE;
+
+ return cmd_results_new(CMD_SUCCESS, NULL);
+}
diff --git a/sway/commands/output.c b/sway/commands/output.c
@@ -8,6 +8,7 @@
// must be in order for the bsearch
static const struct cmd_handler output_handlers[] = {
{ "adaptive_sync", output_cmd_adaptive_sync },
+ { "allow_tearing", output_cmd_allow_tearing },
{ "background", output_cmd_background },
{ "bg", output_cmd_background },
{ "color_profile", output_cmd_color_profile },
diff --git a/sway/commands/output/allow_tearing.c b/sway/commands/output/allow_tearing.c
@@ -0,0 +1,23 @@
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "util.h"
+
+struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) {
+ if (!config->handler_context.output_config) {
+ return cmd_results_new(CMD_FAILURE, "Missing output config");
+ }
+ if (argc == 0) {
+ return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument");
+ }
+
+ if (parse_boolean(argv[0],
+ (config->handler_context.output_config->allow_tearing == 1))) {
+ config->handler_context.output_config->allow_tearing = 1;
+ } else {
+ config->handler_context.output_config->allow_tearing = 0;
+ }
+
+ config->handler_context.leftovers.argc = argc - 1;
+ config->handler_context.leftovers.argv = argv + 1;
+ return NULL;
+}
diff --git a/sway/config/output.c b/sway/config/output.c
@@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) {
oc->set_color_transform = false;
oc->color_transform = NULL;
oc->power = -1;
+ oc->allow_tearing = -1;
return oc;
}
@@ -216,6 +217,9 @@ static void merge_output_config(struct output_config *dst, struct output_config
if (src->power != -1) {
dst->power = src->power;
}
+ if (src->allow_tearing != -1) {
+ dst->allow_tearing = src->allow_tearing;
+ }
}
void store_output_config(struct output_config *oc) {
@@ -258,11 +262,11 @@ void store_output_config(struct output_config *oc) {
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
- "(max render time: %d)",
+ "(max render time: %d) (allow tearing: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->power,
- oc->max_render_time);
+ oc->max_render_time, oc->allow_tearing);
// If the configuration was not merged into an existing configuration, add
// it to the list. Otherwise we're done with it and can free it.
@@ -574,6 +578,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
wlr_color_transform_unref(output->color_transform);
output->color_transform = oc->color_transform;
}
+
+ if (oc && oc->allow_tearing >= 0) {
+ sway_log(SWAY_DEBUG, "Set %s allow tearing to %d",
+ oc->name, oc->allow_tearing);
+ output->allow_tearing = oc->allow_tearing;
+ }
+
return true;
}
@@ -594,6 +605,7 @@ static void default_output_config(struct output_config *oc,
oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->max_render_time = 0;
+ oc->allow_tearing = 0;
}
// find_output_config returns a merged output_config containing all stored
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
@@ -232,6 +232,23 @@ static void output_configure_scene(struct sway_output *output,
}
}
+static bool output_can_tear(struct sway_output *output) {
+ struct sway_workspace *workspace = output->current.active_workspace;
+ if (!workspace) {
+ return false;
+ }
+
+ struct sway_container *fullscreen_con = root->fullscreen_global;
+ if (!fullscreen_con) {
+ fullscreen_con = workspace->current.fullscreen;
+ }
+ if (fullscreen_con && fullscreen_con->view) {
+ return (output->allow_tearing && view_can_tear(fullscreen_con->view));
+ }
+
+ return false;
+}
+
static int output_repaint_timer_handler(void *data) {
struct sway_output *output = data;
@@ -275,6 +292,17 @@ static int output_repaint_timer_handler(void *data) {
wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL);
}
}
+
+ if (output_can_tear(output)) {
+ pending.tearing_page_flip = true;
+
+ if (!wlr_output_test_state(output->wlr_output, &pending)) {
+ sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip",
+ output->wlr_output->name);
+
+ pending.tearing_page_flip = false;
+ }
+ }
if (!wlr_output_commit_state(output->wlr_output, &pending)) {
sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name);
diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c
@@ -0,0 +1,62 @@
+#include <wayland-server-core.h>
+#include <wlr/types/wlr_tearing_control_v1.h>
+#include "sway/server.h"
+#include "sway/tree/view.h"
+#include "sway/output.h"
+#include "log.h"
+
+struct sway_tearing_controller {
+ struct wlr_tearing_control_v1 *tearing_control;
+ struct wl_listener set_hint;
+ struct wl_listener destroy;
+
+ struct wl_list link; // sway_server::tearing_controllers
+};
+
+static void handle_tearing_controller_set_hint(struct wl_listener *listener,
+ void *data) {
+ struct sway_tearing_controller *controller =
+ wl_container_of(listener, controller, set_hint);
+
+ struct sway_view *view = view_from_wlr_surface(
+ controller->tearing_control->surface);
+ if (view) {
+ view->tearing_hint = controller->tearing_control->current;
+ }
+}
+
+static void handle_tearing_controller_destroy(struct wl_listener *listener,
+ void *data) {
+ struct sway_tearing_controller *controller =
+ wl_container_of(listener, controller, destroy);
+ wl_list_remove(&controller->link);
+ free(controller);
+}
+
+void handle_new_tearing_hint(struct wl_listener *listener,
+ void *data) {
+ struct sway_server *server =
+ wl_container_of(listener, server, tearing_control_new_object);
+ struct wlr_tearing_control_v1 *tearing_control = data;
+
+ enum wp_tearing_control_v1_presentation_hint hint =
+ wlr_tearing_control_manager_v1_surface_hint_from_surface(
+ server->tearing_control_v1, tearing_control->surface);
+ sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p",
+ hint, tearing_control->surface);
+
+ struct sway_tearing_controller *controller =
+ calloc(1, sizeof(struct sway_tearing_controller));
+ if (!controller) {
+ return;
+ }
+
+ controller->tearing_control = tearing_control;
+ controller->set_hint.notify = handle_tearing_controller_set_hint;
+ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
+ controller->destroy.notify = handle_tearing_controller_destroy;
+ wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
+ wl_list_init(&controller->link);
+
+ wl_list_insert(&server->tearing_controllers, &controller->link);
+}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
@@ -399,6 +399,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
}
json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time));
+
+ json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing));
}
json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
@@ -593,6 +595,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time));
+ json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view)));
+
json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view)));
json_object_object_add(object, "inhibit_idle",
diff --git a/sway/meson.build b/sway/meson.build
@@ -18,6 +18,7 @@ sway_sources = files(
'desktop/idle_inhibit_v1.c',
'desktop/layer_shell.c',
'desktop/output.c',
+ 'desktop/tearing.c',
'desktop/transaction.c',
'desktop/xdg_shell.c',
'desktop/launcher.c',
@@ -41,6 +42,7 @@ sway_sources = files(
'config/seat.c',
'config/input.c',
+ 'commands/allow_tearing.c',
'commands/assign.c',
'commands/bar.c',
'commands/bind.c',
@@ -188,6 +190,7 @@ sway_sources = files(
'commands/input/xkb_variant.c',
'commands/output/adaptive_sync.c',
+ 'commands/output/allow_tearing.c',
'commands/output/background.c',
'commands/output/disable.c',
'commands/output/dpms.c',
diff --git a/sway/server.c b/sway/server.c
@@ -371,6 +371,13 @@ bool server_init(struct sway_server *server) {
server->content_type_manager_v1 =
wlr_content_type_manager_v1_create(server->wl_display, 1);
wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
+
+ server->tearing_control_v1 =
+ wlr_tearing_control_manager_v1_create(server->wl_display, 1);
+ server->tearing_control_new_object.notify = handle_new_tearing_hint;
+ wl_signal_add(&server->tearing_control_v1->events.new_object,
+ &server->tearing_control_new_object);
+ wl_list_init(&server->tearing_controllers);
struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display);
diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd
@@ -190,6 +190,26 @@ must be separated by one space. For example:
may have no effect or produce unexpected output when used together with future
HDR support features.
+*output* <name> allow_tearing yes|no
+ Allows or disallows screen tearing as a result of immediate page flips,
+ and an immediate presentation mode from a client. The default is that no
+ screen tearing is allowed.
+
+ With immediate page flips, frames from the client are presented as soon
+ as possible instead of synchronizing with the monitor's vblank interval
+ (VSync).
+
+ It is recommended to set *max_render_time* to *off*. In that case a page flip
+ happens as soon as a client updates. Otherwise, tearing will only happen if
+ rendering takes longer than the configured milliseconds before the next
+ display refresh.
+
+ To adjust whether tearing is allowed for specific applications, see
+ *allow_tearing* in *sway*(5). Note that tearing will only be enabled
+ when it's allowed for both the output and the application.
+
+ This setting only has effect when a window is fullscreen on the output.
+
# SEE ALSO
*sway*(5) *sway-input*(5)
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
@@ -215,6 +215,20 @@ set|plus|minus|toggle <amount>
effect on the output the window is currently on. See *sway-output*(5) for
further details.
+*allow_tearing* yes|no
+ Allows or disallows screen tearing as a result of immediate page flips
+ for a fullscreen application.
+
+ When this option is not set, the tearing hints provided by the application
+ determine whether tearing is allowed. When _yes_ is specified,
+ the application allows tearing regardless of the tearing hints.
+ When _no_ is specified, tearing will never be allowed on the application,
+ regardless of the tearing hints.
+
+ This setting only has an effect if tearing is allowed on the output through
+ the per-output *allow_tearing* setting. See *sway-output*(5)
+ for further details.
+
*move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. The optional _px_
argument specifies how many pixels to move the container. If unspecified,
diff --git a/sway/tree/view.c b/sway/tree/view.c
@@ -58,6 +58,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type,
view->executed_criteria = create_list();
view->allow_request_urgent = true;
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
+ view->tearing_mode = TEARING_WINDOW_HINT;
wl_signal_init(&view->events.unmap);
return true;
}
@@ -1260,6 +1261,19 @@ bool view_is_transient_for(struct sway_view *child,
child->impl->is_transient_for(child, ancestor);
}
+bool view_can_tear(struct sway_view *view) {
+ switch (view->tearing_mode) {
+ case TEARING_OVERRIDE_FALSE:
+ return false;
+ case TEARING_OVERRIDE_TRUE:
+ return true;
+ case TEARING_WINDOW_HINT:
+ return view->tearing_hint ==
+ WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
+ }
+ return false;
+}
+
static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
int x, int y, void *data) {
struct timespec *when = data;
diff --git a/swaymsg/main.c b/swaymsg/main.c
@@ -193,7 +193,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "current_workspace", &ws);
json_object_object_get_ex(o, "non_desktop", &non_desktop);
json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
- *transform, *max_render_time, *adaptive_sync_status;
+ *transform, *max_render_time, *adaptive_sync_status, *allow_tearing;
json_object_object_get_ex(o, "make", &make);
json_object_object_get_ex(o, "model", &model);
json_object_object_get_ex(o, "serial", &serial);
@@ -203,6 +203,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "transform", &transform);
json_object_object_get_ex(o, "max_render_time", &max_render_time);
json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status);
+ json_object_object_get_ex(o, "allow_tearing", &allow_tearing);
json_object *x, *y;
json_object_object_get_ex(rect, "x", &x);
json_object_object_get_ex(rect, "y", &y);
@@ -256,6 +257,9 @@ static void pretty_print_output(json_object *o) {
printf(" Adaptive sync: %s\n",
json_object_get_string(adaptive_sync_status));
+
+ printf(" Allow tearing: %s\n",
+ json_object_get_boolean(allow_tearing) ? "yes" : "no");
} else {
printf(
"Output %s '%s %s %s' (disabled)\n",