Add support for tearing-control-v1

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3871
This commit is contained in:
Ricardo Steijn 2023-05-20 16:49:19 +02:00
parent b881c2e84c
commit 85c2167063
20 changed files with 250 additions and 3 deletions

View file

@ -184,6 +184,7 @@ sway_cmd cmd_sticky;
sway_cmd cmd_swaybg_command; sway_cmd cmd_swaybg_command;
sway_cmd cmd_swaynag_command; sway_cmd cmd_swaynag_command;
sway_cmd cmd_swap; sway_cmd cmd_swap;
sway_cmd cmd_tearing_allowed;
sway_cmd cmd_tiling_drag; sway_cmd cmd_tiling_drag;
sway_cmd cmd_tiling_drag_threshold; sway_cmd cmd_tiling_drag_threshold;
sway_cmd cmd_title_align; sway_cmd cmd_title_align;
@ -297,6 +298,7 @@ sway_cmd output_cmd_render_bit_depth;
sway_cmd output_cmd_scale; sway_cmd output_cmd_scale;
sway_cmd output_cmd_scale_filter; sway_cmd output_cmd_scale_filter;
sway_cmd output_cmd_subpixel; sway_cmd output_cmd_subpixel;
sway_cmd output_cmd_tearing_allowed;
sway_cmd output_cmd_toggle; sway_cmd output_cmd_toggle;
sway_cmd output_cmd_transform; sway_cmd output_cmd_transform;
sway_cmd output_cmd_unplug; sway_cmd output_cmd_unplug;

View file

@ -289,6 +289,7 @@ struct output_config {
enum render_bit_depth render_bit_depth; enum render_bit_depth render_bit_depth;
bool set_color_transform; bool set_color_transform;
struct wlr_color_transform *color_transform; struct wlr_color_transform *color_transform;
int tearing_allowed;
char *background; char *background;
char *background_option; char *background_option;

View file

@ -73,6 +73,7 @@ struct sway_output {
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
struct wl_event_source *repaint_timer; struct wl_event_source *repaint_timer;
bool gamma_lut_changed; bool gamma_lut_changed;
bool tearing_allowed;
}; };
struct sway_output_non_desktop { struct sway_output_non_desktop {

View file

@ -116,6 +116,10 @@ struct sway_server {
struct wl_listener request_set_cursor_shape; 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 struct wl_list pending_launcher_ctxs; // launcher_ctx::link
// The timeout for transactions, after which a transaction is applied // 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 set_rr_scheduling(void);
void handle_new_tearing_hint(struct wl_listener *listener, void *data);
#endif #endif

View file

@ -34,6 +34,12 @@ enum sway_view_prop {
#endif #endif
}; };
enum sway_view_tearing_mode {
TEARING_OVERRIDE_FALSE,
TEARING_OVERRIDE_TRUE,
TEARING_WINDOW_HINT,
};
struct sway_view_impl { struct sway_view_impl {
void (*get_constraints)(struct sway_view *view, double *min_width, void (*get_constraints)(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height); double *max_width, double *min_height, double *max_height);
@ -111,6 +117,9 @@ struct sway_view {
int max_render_time; // In milliseconds int max_render_time; // In milliseconds
enum seat_config_shortcuts_inhibit shortcuts_inhibit; enum seat_config_shortcuts_inhibit shortcuts_inhibit;
enum sway_view_tearing_mode tearing_mode;
bool tearing_hint;
}; };
struct sway_xdg_shell_view { struct sway_xdg_shell_view {
@ -335,4 +344,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
void view_send_frame_done(struct sway_view *view); void view_send_frame_done(struct sway_view *view);
bool view_can_tear(struct sway_view *view);
#endif #endif

View file

@ -14,6 +14,7 @@ protocols = [
wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', 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/content-type/content-type-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-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', 'wlr-layer-shell-unstable-v1.xml',
'idle.xml', 'idle.xml',
'wlr-output-power-management-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml',

View file

@ -136,6 +136,7 @@ static const struct cmd_handler command_handlers[] = {
{ "splitv", cmd_splitv }, { "splitv", cmd_splitv },
{ "sticky", cmd_sticky }, { "sticky", cmd_sticky },
{ "swap", cmd_swap }, { "swap", cmd_swap },
{ "tearing_allowed", cmd_tearing_allowed },
{ "title_format", cmd_title_format }, { "title_format", cmd_title_format },
{ "unmark", cmd_unmark }, { "unmark", cmd_unmark },
{ "urgent", cmd_urgent }, { "urgent", cmd_urgent },

View file

@ -26,6 +26,7 @@ static const struct cmd_handler output_handlers[] = {
{ "scale", output_cmd_scale }, { "scale", output_cmd_scale },
{ "scale_filter", output_cmd_scale_filter }, { "scale_filter", output_cmd_scale_filter },
{ "subpixel", output_cmd_subpixel }, { "subpixel", output_cmd_subpixel },
{ "tearing_allowed", output_cmd_tearing_allowed },
{ "toggle", output_cmd_toggle }, { "toggle", output_cmd_toggle },
{ "transform", output_cmd_transform }, { "transform", output_cmd_transform },
{ "unplug", output_cmd_unplug }, { "unplug", output_cmd_unplug },

View file

@ -0,0 +1,22 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "util.h"
struct cmd_results *output_cmd_tearing_allowed(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 tearing_allowed argument");
}
if (parse_boolean(argv[0], true)) {
config->handler_context.output_config->tearing_allowed = 1;
} else {
config->handler_context.output_config->tearing_allowed = 0;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
}

View file

@ -0,0 +1,25 @@
#include <sway/commands.h>
#include "sway/config.h"
#include "sway/tree/view.h"
#include "util.h"
struct cmd_results *cmd_tearing_allowed(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "tearing_allowed", 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);
}

View file

@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) {
oc->set_color_transform = false; oc->set_color_transform = false;
oc->color_transform = NULL; oc->color_transform = NULL;
oc->power = -1; oc->power = -1;
oc->tearing_allowed = -1;
return oc; return oc;
} }
@ -216,6 +217,9 @@ static void merge_output_config(struct output_config *dst, struct output_config
if (src->power != -1) { if (src->power != -1) {
dst->power = src->power; dst->power = src->power;
} }
if (src->tearing_allowed != -1) {
dst->tearing_allowed = src->tearing_allowed;
}
} }
void store_output_config(struct output_config *oc) { 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 " 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) " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
"(max render time: %d)", "(max render time: %d) (tearing allowed: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, 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->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->power, oc->transform, oc->background, oc->background_option, oc->power,
oc->max_render_time); oc->max_render_time, oc->tearing_allowed);
// If the configuration was not merged into an existing configuration, add // 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. // 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); wlr_color_transform_unref(output->color_transform);
output->color_transform = oc->color_transform; output->color_transform = oc->color_transform;
} }
if (oc && oc->tearing_allowed >= 0) {
sway_log(SWAY_DEBUG, "Set %s tearing allowed to %d",
oc->name, oc->tearing_allowed);
output->tearing_allowed = oc->tearing_allowed;
}
return true; return true;
} }
@ -594,6 +605,7 @@ static void default_output_config(struct output_config *oc,
oc->subpixel = output->detected_subpixel; oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->max_render_time = 0; oc->max_render_time = 0;
oc->tearing_allowed = 0;
} }
// find_output_config returns a merged output_config containing all stored // find_output_config returns a merged output_config containing all stored

View file

@ -85,6 +85,20 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
return focus->sway_workspace; return focus->sway_workspace;
} }
bool output_can_tear_fullscreen_view(struct sway_output *output,
struct sway_view *view) {
if (!view) {
return false;
}
#ifdef WLR_HAS_DRM_BACKEND
if (wlr_backend_is_drm(output->wlr_output->backend) &&
output->tearing_allowed && view_can_tear(view)) {
return true;
}
#endif
return false;
}
struct send_frame_done_data { struct send_frame_done_data {
struct timespec when; struct timespec when;
int msec_until_refresh; int msec_until_refresh;
@ -146,6 +160,7 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
struct send_frame_done_data *data = user_data; struct send_frame_done_data *data = user_data;
struct sway_output *output = data->output; struct sway_output *output = data->output;
int view_max_render_time = 0; int view_max_render_time = 0;
bool view_can_tear = false;
if (buffer->primary_output != data->output->scene_output) { if (buffer->primary_output != data->output->scene_output) {
return; return;
@ -157,6 +172,10 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
SWAY_SCENE_DESC_VIEW); SWAY_SCENE_DESC_VIEW);
if (view) { if (view) {
view_max_render_time = view->max_render_time; view_max_render_time = view->max_render_time;
if (view->container && container_is_fullscreen_or_child(view->container)) {
view_can_tear = output_can_tear_fullscreen_view(output, view);
}
break; break;
} }
@ -170,6 +189,10 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer,
int delay = data->msec_until_refresh - output->max_render_time int delay = data->msec_until_refresh - output->max_render_time
- view_max_render_time; - view_max_render_time;
if (view_can_tear) {
delay = 0;
}
struct buffer_timer *timer = NULL; struct buffer_timer *timer = NULL;
if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
@ -232,6 +255,24 @@ static void output_configure_scene(struct sway_output *output,
} }
} }
static struct sway_view *output_get_fullscreen_view(
struct sway_output *output) {
struct sway_workspace *workspace = output->current.active_workspace;
if (!workspace) {
return NULL;
}
struct sway_container *fullscreen_con = root->fullscreen_global;
if (!fullscreen_con) {
fullscreen_con = workspace->current.fullscreen;
}
if (fullscreen_con) {
return fullscreen_con->view;
}
return NULL;
}
static int output_repaint_timer_handler(void *data) { static int output_repaint_timer_handler(void *data) {
struct sway_output *output = data; struct sway_output *output = data;
@ -330,6 +371,11 @@ static void handle_frame(struct wl_listener *listener, void *user_data) {
int delay = msec_until_refresh - output->max_render_time; int delay = msec_until_refresh - output->max_render_time;
struct sway_view *fullscreen_view = output_get_fullscreen_view(output);
if (output_can_tear_fullscreen_view(output, fullscreen_view)) {
delay = 0;
}
// If the delay is less than 1 millisecond (which is the least we can wait) // If the delay is less than 1 millisecond (which is the least we can wait)
// then just render right away. // then just render right away.
if (delay < 1) { if (delay < 1) {

60
sway/desktop/tearing.c Normal file
View file

@ -0,0 +1,60 @@
#include <wayland-server-core.h>
#include "sway/server.h"
#include "sway/tree/view.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->hint;
}
}
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);
}

View file

@ -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, "max_render_time", json_object_new_int(output->max_render_time));
json_object_object_add(object, "tearing_allowed", json_object_new_boolean(output->tearing_allowed));
} }
json_object *ipc_json_describe_disabled_output(struct sway_output *output) { 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, "max_render_time", json_object_new_int(c->view->max_render_time));
json_object_object_add(object, "tearing_allowed", 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, "shell", json_object_new_string(view_get_shell(c->view)));
json_object_object_add(object, "inhibit_idle", json_object_object_add(object, "inhibit_idle",

View file

@ -18,6 +18,7 @@ sway_sources = files(
'desktop/idle_inhibit_v1.c', 'desktop/idle_inhibit_v1.c',
'desktop/layer_shell.c', 'desktop/layer_shell.c',
'desktop/output.c', 'desktop/output.c',
'desktop/tearing.c',
'desktop/transaction.c', 'desktop/transaction.c',
'desktop/xdg_shell.c', 'desktop/xdg_shell.c',
'desktop/launcher.c', 'desktop/launcher.c',
@ -110,6 +111,7 @@ sway_sources = files(
'commands/swaybg_command.c', 'commands/swaybg_command.c',
'commands/swaynag_command.c', 'commands/swaynag_command.c',
'commands/swap.c', 'commands/swap.c',
'commands/tearing_allowed.c',
'commands/tiling_drag.c', 'commands/tiling_drag.c',
'commands/tiling_drag_threshold.c', 'commands/tiling_drag_threshold.c',
'commands/title_align.c', 'commands/title_align.c',
@ -200,6 +202,7 @@ sway_sources = files(
'commands/output/scale.c', 'commands/output/scale.c',
'commands/output/scale_filter.c', 'commands/output/scale_filter.c',
'commands/output/subpixel.c', 'commands/output/subpixel.c',
'commands/output/tearing_allowed.c',
'commands/output/toggle.c', 'commands/output/toggle.c',
'commands/output/transform.c', 'commands/output/transform.c',
'commands/output/unplug.c', 'commands/output/unplug.c',

View file

@ -372,6 +372,13 @@ bool server_init(struct sway_server *server) {
wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_content_type_manager_v1_create(server->wl_display, 1);
wlr_fractional_scale_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 = struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_registry_create(server->wl_display);
wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);

View file

@ -190,6 +190,19 @@ must be separated by one space. For example:
may have no effect or produce unexpected output when used together with future may have no effect or produce unexpected output when used together with future
HDR support features. HDR support features.
*output* <name> tearing_allowed yes|no
Allows or disallows screen tearing as a result of asynchronous page flips,
and an immediate presentation mode from a client.
With asynchronous page flips, frames from the client are presented as soon
as possible instead of synchronizing with the monitor's vblank interval
(VSync). This prevents stutter and reduces latency in games.
To adjust whether tearing is allowed for specific applications, see
*tearing_allowed* in *sway*(5).
This setting only has effect on fullscreen windows.
# SEE ALSO # SEE ALSO
*sway*(5) *sway-input*(5) *sway*(5) *sway-input*(5)

View file

@ -353,6 +353,20 @@ set|plus|minus|toggle <amount>
becomes fullscreen on the same workspace as the first container. In either becomes fullscreen on the same workspace as the first container. In either
of those cases, the second container will gain focus. of those cases, the second container will gain focus.
*tearing_allowed* yes|no
Allows or disallows screen tearing as a result of asynchronous 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 *tearing_allowed* setting. See *sway-output*(5)
for further details.
*title_format* <format> *title_format* <format>
Sets the format of window titles. The following placeholders may be used: Sets the format of window titles. The following placeholders may be used:

View file

@ -58,6 +58,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type,
view->executed_criteria = create_list(); view->executed_criteria = create_list();
view->allow_request_urgent = true; view->allow_request_urgent = true;
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
view->tearing_mode = TEARING_WINDOW_HINT;
wl_signal_init(&view->events.unmap); wl_signal_init(&view->events.unmap);
return true; return true;
} }
@ -1260,6 +1261,18 @@ bool view_is_transient_for(struct sway_view *child,
child->impl->is_transient_for(child, ancestor); 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;
}
return false;
}
static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
int x, int y, void *data) { int x, int y, void *data) {
struct timespec *when = data; struct timespec *when = data;

View file

@ -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, "current_workspace", &ws);
json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object_object_get_ex(o, "non_desktop", &non_desktop);
json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
*transform, *max_render_time, *adaptive_sync_status; *transform, *max_render_time, *adaptive_sync_status, *tearing_allowed;
json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "make", &make);
json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "model", &model);
json_object_object_get_ex(o, "serial", &serial); 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, "transform", &transform);
json_object_object_get_ex(o, "max_render_time", &max_render_time); 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, "adaptive_sync_status", &adaptive_sync_status);
json_object_object_get_ex(o, "tearing_allowed", &tearing_allowed);
json_object *x, *y; json_object *x, *y;
json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "x", &x);
json_object_object_get_ex(rect, "y", &y); 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", printf(" Adaptive sync: %s\n",
json_object_get_string(adaptive_sync_status)); json_object_get_string(adaptive_sync_status));
printf(" Tearing allowed: %s\n",
json_object_get_boolean(tearing_allowed) ? "yes" : "no");
} else { } else {
printf( printf(
"Output %s '%s %s %s' (disabled)\n", "Output %s '%s %s %s' (disabled)\n",