mirror of
https://github.com/swaywm/sway.git
synced 2024-11-09 18:03:14 +00:00
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.
This commit is contained in:
parent
b881c2e84c
commit
9a1c411abd
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -116,6 +116,10 @@ struct sway_server {
|
|||
|
||||
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
|
||||
|
||||
// The timeout for transactions, after which a transaction is applied
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 },
|
||||
|
|
24
sway/commands/allow_tearing.c
Normal file
24
sway/commands/allow_tearing.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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 },
|
||||
|
|
23
sway/commands/output/allow_tearing.c
Normal file
23
sway/commands/output/allow_tearing.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -276,6 +293,17 @@ static int output_repaint_timer_handler(void *data) {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
62
sway/desktop/tearing.c
Normal file
62
sway/desktop/tearing.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -372,6 +372,13 @@ bool server_init(struct sway_server *server) {
|
|||
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);
|
||||
wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue