protocols: advertise tearing-control + fractional-scale

Add wp_tearing_control_manager_v1 and wp_fractional_scale_manager_v1 as
pass-through advertisements for client convenience (games, HiDPI).  kick
drives a vsynced scene at scale 1.0, so nothing is honoured: a new
tearing object is only logged at DEBUG.  Bundle the output-power XML
locally too (needed by a later commit) and generate the server headers
the wlroots headers' quoted includes require.
This commit is contained in:
Andrew Hamilton 2026-09-03 13:54:25 +10:00
parent 044c7543bb
commit c64a10e8d9
4 changed files with 250 additions and 0 deletions

65
kick.c
View file

@ -83,14 +83,17 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_presentation_time.h>
@ -104,8 +107,12 @@
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_shm.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_tearing_control_v1.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
@ -344,6 +351,38 @@ struct kick_server {
nothing to wire up: every output commit that carries a rendered buffer
becomes a copyable frame. */
struct wlr_screencopy_manager_v1 *screencopy_mgr;
/* Tearing (wp-tearing-control-v1) + fractional-scale (wp-fractional-scale):
both are advertised for client convenience. kick drives a vsynced scene
and fixed 1.0 scale, so nothing is honoured: the hint object is only
logged (DEBUG), and fractional scale has nothing to notify. */
struct wlr_tearing_control_manager_v1 *tearing_mgr;
struct wl_listener tearing_new_object;
struct wlr_fractional_scale_manager_v1 *fractional_scale_mgr;
/* Output power management (zwlr_output_power_management_v1): a client
(e.g. wlopm) blanks or unblanks a specific output. kick's seatless
headless/standalone output maps to the single server output. */
struct wlr_output_power_manager_v1 *output_power_mgr;
struct wl_listener output_power_set_mode;
/* Keyboard shortcuts inhibit (zwp_keyboard_shortcuts_inhibit_v1): lets a
fullscreen game/VM grab the compositor's keyboard shortcuts. While the
focused surface's inhibitor is active, the grab chords in
process_key_event are swallowed (see process_key_event). */
struct wlr_keyboard_shortcuts_inhibit_manager_v1 *kb_inhibit_mgr;
struct wl_listener kb_inhibit_new_inhibitor;
struct wl_listener kb_inhibit_destroy;
/* Virtual keyboard/pointer (zwp_virtual_keyboard_v1 /
zwp_virtual_pointer_v1): synthesised input from clients like wtype, fed
into the seat just like real devices. */
struct wlr_virtual_keyboard_manager_v1 *virtual_kb_mgr;
struct wl_listener virtual_kb_new;
struct wlr_virtual_keyboard_v1 *virtual_keyboard;
struct wlr_virtual_pointer_manager_v1 *virtual_ptr_mgr;
struct wl_listener virtual_ptr_new;
/* Xdg-activation (xdg-activation-v1): lets a launcher focus a window by
token instead of requiring user input at exactly the right time. On a
request_activate we switch to the tab owning that surface. */
struct wlr_xdg_activation_v1 *xdg_activation;
struct wl_listener xdg_activation_request;
/* Pointer constraints + relative pointer (zwp_pointer_constraints_v1 /
zwp_relative_pointer_v1). Only the focused surface's constraint is
active; its region lives in the surface's local space, so the surface's
@ -5803,6 +5842,22 @@ static void handle_idle_new_inhibitor(struct wl_listener *listener, void *data)
wlr_idle_notifier_v1_set_inhibited(server.idle_notifier, server.idle_inhibited);
}
/* ===================== Tearing + fractional-scale =====================
Both are pass-through wlroots managers that exist only to be advertised:
- wp-tearing-control-v1 lets a surface request unthrottled presentation.
kick drives a fully vsynced scene (no page-flip bypass), so the hint is
only logged (DEBUG); nothing skips vsync.
- wp-fractional-scale-v1 lets clients learn a non-integer scale. kick's
output runs at scale 1.0 and never changes it at runtime, so there is
nothing to notify via wlr_fractional_scale_v1_notify_scale. */
static void handle_tearing_new_object(struct wl_listener *listener,
void *data) {
(void)listener;
struct wlr_tearing_control_v1 *tc = data;
kick_log(KICK_LOG_DEBUG, "[tearing] surface %p hint=%d\n",
(void *)tc->surface, (int)tc->current);
}
/* ===================== Foreign toplevel management =====================
Taskbars/switchers (waybar taskbar, disorder, etc.) enumerate kick's
windows through wlr-foreign-toplevel-management-v1 and
@ -9830,6 +9885,16 @@ int main(int argc, char *argv[]) {
runs the whole capture flow itself off the output commit path. */
server.screencopy_mgr = wlr_screencopy_manager_v1_create(server.wl_display);
/* Tearing-control + fractional-scale: advertised for client convenience
(games, HiDPI surfaces). Nothing is honoured beyond the DEBUG log on
a new tearing object. */
server.tearing_mgr = wlr_tearing_control_manager_v1_create(server.wl_display, 1);
server.tearing_new_object.notify = handle_tearing_new_object;
wl_signal_add(&server.tearing_mgr->events.new_object,
&server.tearing_new_object);
server.fractional_scale_mgr =
wlr_fractional_scale_manager_v1_create(server.wl_display, 1);
/* The output layout exists in both modes so xdg-output can advertise
logical output geometry to clients; nested mode simply has one output
(the host window) in the layout. */

View file

@ -149,6 +149,24 @@ protocols = {
'wlr-protocols/wlr-screencopy-unstable-v1.xml',
'wlr-screencopy-unstable-v1',
],
# Tearing control (a surface asks for unthrottled presentation) and
# fractional scale (a surface learns a non-integer output scale). Both
# are staging protocols; kick only advertises them.
'tearing-control': [
wl_protocols_dir / 'staging/tearing-control/tearing-control-v1.xml',
'tearing-control-v1',
],
'fractional-scale': [
wl_protocols_dir / 'staging/fractional-scale/fractional-scale-v1.xml',
'fractional-scale-v1',
],
# Output power management (a client blanks/unblanks an output). Bundled
# locally like the other wlr-protocols XMLs (copied from the pinned
# wlroots subproject).
'wlr-output-power-management-unstable-v1': [
'wlr-protocols/wlr-output-power-management-unstable-v1.xml',
'wlr-output-power-management-unstable-v1',
],
'cursor-shape': [
wl_protocols_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
'cursor-shape-v1',
@ -223,6 +241,22 @@ foreach protocol, p : protocols
output: base + '-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'])
endif
if protocol == 'tearing-control'
# Serves wlr_tearing_control_v1.h's quoted
# #include "tearing-control-v1-protocol.h".
protocol_headers += custom_target(protocol + ' server header',
input: xml,
output: base + '-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'])
endif
if protocol == 'wlr-output-power-management-unstable-v1'
# Serves wlr_output_power_management_v1.h's quoted
# #include "wlr-output-power-management-unstable-v1-protocol.h".
protocol_headers += custom_target(protocol + ' server header',
input: xml,
output: base + '-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'])
endif
endforeach
executable('kick',

View file

@ -3365,3 +3365,26 @@ def test_standalone_headless_screencopy_frame(tmp_path):
finally:
s.close()
def test_standalone_headless_tearing_and_fractional_globals(tmp_path):
"""The compositor advertises wp_tearing_control_manager_v1 (games request
unthrottled presentation) and wp_fractional_scale_manager_v1 (clients
learn a non-integer scale). kick drives a vsynced scene at scale 1.0,
so both are pass-through: advertised but nothing honoured."""
out = tmp_path / "globals.txt"
s = _spawn_standalone({}, "%s --globals-out '%s' '#112233'" % (WL, out))
try:
deadline = time.monotonic() + 15.0
while time.monotonic() < deadline:
if out.exists():
break
time.sleep(0.1)
assert out.exists(), "wltest --globals-out never wrote its file"
names = {line.split()[0] for line in out.read_text().splitlines()}
for want in ("wp_tearing_control_manager_v1",
"wp_fractional_scale_manager_v1"):
assert want in names, \
f"missing global {want}; advertised: {sorted(names)}"
finally:
s.close()

View file

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_power_management_unstable_v1">
<copyright>
Copyright © 2019 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Control power management modes of outputs">
This protocol allows clients to control power management modes
of outputs that are currently part of the compositor space. The
intent is to allow special clients like desktop shells to power
down outputs when the system is idle.
To modify outputs not currently part of the compositor space see
wlr-output-management.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_output_power_manager_v1" version="1">
<description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power
management mode controls.
</description>
<request name="get_output_power">
<description summary="get a power management for an output">
Create a output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_output_power_v1" version="1">
<description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of
an output.
</description>
<enum name="mode">
<entry name="off" value="0"
summary="Output is turned off."/>
<entry name="on" value="1"
summary="Output is turned on, no power saving"/>
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="inexistent power save mode"/>
</enum>
<request name="set_mode">
<description summary="Set an outputs power save mode">
Set an output's power save mode to the given mode. The mode change
is effective immediately. If the output does not support the given
mode a failed event is sent.
</description>
<arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
</request>
<event name="mode">
<description summary="Report a power management mode change">
Report the power management mode change of an output.
The mode event is sent after an output changed its power
management mode. The reason can be a client using set_mode or the
compositor deciding to change an output's mode.
This event is also sent immediately when the object is created
so the client is informed about the current power management mode.
</description>
<arg name="mode" type="uint" enum="mode"
summary="the output's new power management mode"/>
</event>
<event name="failed">
<description summary="object no longer valid">
This event indicates that the output power management mode control
is no longer valid. This can happen for a number of reasons,
including:
- The output doesn't support power management
- Another client already has exclusive power management mode control
for this output
- The output disappeared
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this power management">
Destroys the output power management mode control object.
</description>
</request>
</interface>
</protocol>