diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index 9ccfb798..cb6fac63 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -187,7 +187,12 @@ Actions are used in menus and keyboard/mouse bindings.
original window. There can be multiple windows with this mode set.
**
- Toggles tearing for the focused window.
+ Toggles tearing for the focused window between enabled and disabled.
+ This overrides the preference (tearing hint) from the focused window.
+
+ Requires the config option 'allowTearing'. When 'allowTearing' is set
+ to 'fullscreen' or 'fullscreenForced', tearing will still only be
+ enabled if the active window is in fullscreen mode.
**
Give focus to topmost window on given output and warp the cursor
diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd
index cfd46316..0edee192 100644
--- a/docs/labwc-config.5.scd
+++ b/docs/labwc-config.5.scd
@@ -185,9 +185,18 @@ this is for compatibility with Openbox.
*fullscreen* enables adaptive sync whenever a window is in fullscreen
mode.
-** [yes|no]
- Allow tearing, if requested by the active window, to reduce input lag.
- Default is no.
+** [yes|no|fullscreen|fullscreenForced]
+ Allow tearing to reduce input lag. Default is no.
+
+ *yes* allows tearing if requested by the active window.
+
+ *fullscreen* allows tearing if requested by the active window, but
+ only when the window is in fullscreen mode.
+
+ *fullscreenForced* enables tearing whenever the active window is in
+ fullscreen mode, whether or not the application has requested tearing.
+
+ Use the *ToggleTearing* action for forcefully enable tearing.
Note: Enabling this option with atomic mode setting is experimental. If
you experience undesirable side effects when tearing is allowed,
diff --git a/include/config/rcxml.h b/include/config/rcxml.h
index 62fb5e57..c49bafd2 100644
--- a/include/config/rcxml.h
+++ b/include/config/rcxml.h
@@ -30,6 +30,13 @@ enum adaptive_sync_mode {
LAB_ADAPTIVE_SYNC_FULLSCREEN,
};
+enum tearing_mode {
+ LAB_TEARING_DISABLED = 0,
+ LAB_TEARING_ENABLED,
+ LAB_TEARING_FULLSCREEN,
+ LAB_TEARING_FULLSCREEN_FORCED,
+};
+
enum tiling_events_mode {
LAB_TILING_EVENTS_NEVER = 0,
LAB_TILING_EVENTS_REGION = 1 << 0,
@@ -54,7 +61,7 @@ struct rcxml {
bool xdg_shell_server_side_deco;
int gap;
enum adaptive_sync_mode adaptive_sync;
- bool allow_tearing;
+ enum tearing_mode allow_tearing;
bool reuse_output_mode;
enum view_placement_policy placement_policy;
bool xwayland_persistence;
diff --git a/include/labwc.h b/include/labwc.h
index 564f668c..7156a5c7 100644
--- a/include/labwc.h
+++ b/include/labwc.h
@@ -531,6 +531,7 @@ struct output *output_nearest_to_cursor(struct server *server);
bool output_is_usable(struct output *output);
void output_update_usable_area(struct output *output);
void output_update_all_usable_areas(struct server *server, bool layout_changed);
+bool output_get_tearing_allowance(struct output *output);
struct wlr_box output_usable_area_in_layout_coords(struct output *output);
struct wlr_box output_usable_area_scaled(struct output *output);
void handle_output_power_manager_set_mode(struct wl_listener *listener,
diff --git a/include/view.h b/include/view.h
index 9dc70aa8..0fde958e 100644
--- a/include/view.h
+++ b/include/view.h
@@ -30,6 +30,12 @@ enum ssd_preference {
LAB_SSD_PREF_SERVER,
};
+enum three_state {
+ LAB_STATE_UNSPECIFIED = 0,
+ LAB_STATE_ENABLED,
+ LAB_STATE_DISABLED
+};
+
/**
* Directions in which a view can be maximized. "None" is used
* internally to mean "not maximized" but is not valid in rc.xml.
@@ -187,6 +193,7 @@ struct view {
enum view_axis maximized;
bool fullscreen;
bool tearing_hint;
+ enum three_state force_tearing;
bool visible_on_all_workspaces;
enum view_edge tiled;
uint32_t edges_visible; /* enum wlr_edges bitset */
diff --git a/src/action.c b/src/action.c
index 0aa45d7a..69a317bb 100644
--- a/src/action.c
+++ b/src/action.c
@@ -1109,9 +1109,22 @@ actions_run(struct view *activator, struct server *server,
break;
case ACTION_TYPE_TOGGLE_TEARING:
if (view) {
- view->tearing_hint = !view->tearing_hint;
- wlr_log(WLR_DEBUG, "tearing %sabled",
- view->tearing_hint ? "en" : "dis");
+ switch (view->force_tearing) {
+ case LAB_STATE_UNSPECIFIED:
+ view->force_tearing =
+ output_get_tearing_allowance(view->output)
+ ? LAB_STATE_DISABLED : LAB_STATE_ENABLED;
+ break;
+ case LAB_STATE_DISABLED:
+ view->force_tearing = LAB_STATE_ENABLED;
+ break;
+ case LAB_STATE_ENABLED:
+ view->force_tearing = LAB_STATE_DISABLED;
+ break;
+ }
+ wlr_log(WLR_ERROR, "force tearing %sabled",
+ view->force_tearing == LAB_STATE_ENABLED
+ ? "en" : "dis");
}
break;
case ACTION_TYPE_TOGGLE_SHADE:
diff --git a/src/config/rcxml.c b/src/config/rcxml.c
index 74388b10..93903caf 100644
--- a/src/config/rcxml.c
+++ b/src/config/rcxml.c
@@ -770,6 +770,20 @@ set_adaptive_sync_mode(const char *str, enum adaptive_sync_mode *variable)
}
}
+static void
+set_tearing_mode(const char *str, enum tearing_mode *variable)
+{
+ if (!strcasecmp(str, "fullscreen")) {
+ *variable = LAB_TEARING_FULLSCREEN;
+ } else if (!strcasecmp(str, "fullscreenForced")) {
+ *variable = LAB_TEARING_FULLSCREEN_FORCED;
+ } else if (parse_bool(str, -1) == 1) {
+ *variable = LAB_TEARING_ENABLED;
+ } else {
+ *variable = LAB_TEARING_DISABLED;
+ }
+}
+
static void
entry(xmlNode *node, char *nodename, char *content)
{
@@ -884,7 +898,7 @@ entry(xmlNode *node, char *nodename, char *content)
} else if (!strcasecmp(nodename, "adaptiveSync.core")) {
set_adaptive_sync_mode(content, &rc.adaptive_sync);
} else if (!strcasecmp(nodename, "allowTearing.core")) {
- set_bool(content, &rc.allow_tearing);
+ set_tearing_mode(content, &rc.allow_tearing);
} else if (!strcasecmp(nodename, "reuseOutputMode.core")) {
set_bool(content, &rc.reuse_output_mode);
} else if (!strcmp(nodename, "policy.placement")) {
diff --git a/src/output.c b/src/output.c
index 8ba8225a..b9ac9855 100644
--- a/src/output.c
+++ b/src/output.c
@@ -38,28 +38,50 @@ get_tearing_retry_count(struct output *output)
return refresh > 0 ? refresh / 500 : 120;
}
-static bool
-get_tearing_preference(struct output *output)
+bool
+output_get_tearing_allowance(struct output *output)
{
struct server *server = output->server;
- /* Never allow tearing when disabled */
+ /* never allow tearing when disabled */
if (!rc.allow_tearing) {
return false;
}
- /* Tearing is only allowed for the output with the active view */
- if (!server->active_view || server->active_view->output != output) {
+ struct view *view = server->active_view;
+
+ /* tearing is only allowed for the output with the active view */
+ if (!view || view->output != output) {
return false;
}
- /* Tearing should not have failed too many times */
+ /* tearing should not have failed too many times */
if (output->nr_tearing_failures >= get_tearing_retry_count(output)) {
return false;
}
- /* If the active view requests tearing, or it is toggled on with action, allow it */
- return server->active_view->tearing_hint;
+ /* allow tearing for any window when requested or forced */
+ if (rc.allow_tearing == LAB_TEARING_ENABLED) {
+ if (view->force_tearing == LAB_STATE_UNSPECIFIED) {
+ return view->tearing_hint;
+ } else {
+ return view->force_tearing == LAB_STATE_ENABLED;
+ }
+ }
+
+ /* remaining tearing options apply only to full-screen windows */
+ if (!view->fullscreen) {
+ return false;
+ }
+
+ if (view->force_tearing == LAB_STATE_UNSPECIFIED) {
+ /* honor the tearing hint or the fullscreen-force preference */
+ return view->tearing_hint ||
+ rc.allow_tearing == LAB_TEARING_FULLSCREEN_FORCED;
+ }
+
+ /* honor tearing as requested by action */
+ return view->force_tearing == LAB_STATE_ENABLED;
}
static void
@@ -128,7 +150,7 @@ output_frame_notify(struct wl_listener *listener, void *data)
struct wlr_scene_output *scene_output = output->scene_output;
struct wlr_output_state *pending = &output->pending;
- pending->tearing_page_flip = get_tearing_preference(output);
+ pending->tearing_page_flip = output_get_tearing_allowance(output);
bool committed =
lab_wlr_scene_output_commit(scene_output, pending);
diff --git a/src/tearing.c b/src/tearing.c
index 583fe1e2..b4e86d60 100644
--- a/src/tearing.c
+++ b/src/tearing.c
@@ -15,8 +15,15 @@ set_tearing_hint(struct wl_listener *listener, void *data)
{
struct tearing_controller *controller = wl_container_of(listener, controller, set_hint);
struct view *view = view_from_wlr_surface(controller->tearing_control->surface);
- if (view && controller->tearing_control->current) {
- view->tearing_hint = true;
+ if (view) {
+ /*
+ * tearing_control->current is actually an enum:
+ * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC = 0
+ * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC = 1
+ *
+ * Using it as a bool here allows us to not ship the XML.
+ */
+ view->tearing_hint = controller->tearing_control->current;
}
}