protocols: honor tearing hints and fractional scale
Drive the output commit's async-flip flag from the shown surface's tearing hint (a real tearing flip on DRM; correctly ignored on headless/nested), with per-object hint tracking that requests a frame on change. Notify each surface of the output scale at claim so clients never guess preferred_scale. wltest gains --tearing and --fractional-out (plus linking the two protocol stubs it now uses); smoke tests cover the async commit transition and the notified scale value.
This commit is contained in:
parent
8a71a062ce
commit
50a660860d
5 changed files with 228 additions and 18 deletions
95
kick.c
95
kick.c
|
|
@ -375,9 +375,9 @@ struct kick_server {
|
|||
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. */
|
||||
the shown surface's hint drives the commit's async-flip flag (see
|
||||
apply_tearing_hint), and each surface learns the output scale at
|
||||
claim. Per-object tearing listeners live in kick_tearing. */
|
||||
struct wlr_tearing_control_manager_v1 *tearing_mgr;
|
||||
struct wl_listener tearing_new_object;
|
||||
struct wlr_fractional_scale_manager_v1 *fractional_scale_mgr;
|
||||
|
|
@ -3630,6 +3630,14 @@ static void claim_xdg_window(struct kick_tab *tab, struct kick_window *w,
|
|||
set_app_id events re-sync the handles). */
|
||||
foreign_handle_create(w);
|
||||
|
||||
/* Tell the surface the output scale up front: clients that wait for
|
||||
preferred_scale before sizing assets would otherwise guess. The
|
||||
scale is fixed per session, so one notification covers the surface. */
|
||||
if (server.fractional_scale_mgr && toplevel->base->surface) {
|
||||
double scale = server.output ? (double)server.output->scale : 1.0;
|
||||
wlr_fractional_scale_v1_notify_scale(toplevel->base->surface, scale);
|
||||
}
|
||||
|
||||
w->request_maximize.notify = handle_request_maximize;
|
||||
wl_signal_add(&toplevel->events.request_maximize, &w->request_maximize);
|
||||
w->request_fullscreen.notify = handle_request_fullscreen;
|
||||
|
|
@ -5951,21 +5959,82 @@ static void handle_idle_new_inhibitor(struct wl_listener *listener, void *data)
|
|||
}
|
||||
|
||||
/* ===================== 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. */
|
||||
The shown tab's focused surface hint drives the output commit's
|
||||
async-flip flag: on DRM this becomes a real tearing page flip (game
|
||||
tabs pair it with content-type=game); headless/nested outputs ignore
|
||||
the flag, correctly, since a tear cannot pass through the host.
|
||||
- wp-fractional-scale-v1 tells a surface the output scale. kick's
|
||||
output scale is fixed per session, so each surface is notified once
|
||||
at claim (from the live output scale, covering HiDPI DRM too). */
|
||||
struct kick_tearing {
|
||||
struct wlr_tearing_control_v1 *tc;
|
||||
struct wl_listener set_hint;
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
static void handle_tearing_hint(struct wl_listener *listener, void *data) {
|
||||
struct kick_tearing *kt = wl_container_of(listener, kt, set_hint);
|
||||
(void)data;
|
||||
kick_log(KICK_LOG_DEBUG, "[tearing] surface %p hint=%d\n",
|
||||
(void *)kt->tc->surface, (int)kt->tc->current);
|
||||
/* The commit path re-reads the hint every frame, but a hint change
|
||||
alone produces no frame: request one so async takes effect (or
|
||||
stops) promptly. */
|
||||
kick_request_redraw();
|
||||
}
|
||||
|
||||
static void handle_tearing_destroy(struct wl_listener *listener, void *data) {
|
||||
struct kick_tearing *kt = wl_container_of(listener, kt, destroy);
|
||||
(void)data;
|
||||
wl_list_remove(&kt->set_hint.link);
|
||||
wl_list_remove(&kt->destroy.link);
|
||||
free(kt);
|
||||
}
|
||||
|
||||
static void handle_tearing_new_object(struct wl_listener *listener,
|
||||
void *data) {
|
||||
(void)listener;
|
||||
struct wlr_tearing_control_v1 *tc = data;
|
||||
struct kick_tearing *kt = calloc(1, sizeof(*kt));
|
||||
if (!kt)
|
||||
return;
|
||||
kt->tc = tc;
|
||||
kt->set_hint.notify = handle_tearing_hint;
|
||||
wl_signal_add(&tc->events.set_hint, &kt->set_hint);
|
||||
kt->destroy.notify = handle_tearing_destroy;
|
||||
wl_signal_add(&tc->events.destroy, &kt->destroy);
|
||||
kick_log(KICK_LOG_DEBUG, "[tearing] surface %p hint=%d\n",
|
||||
(void *)tc->surface, (int)tc->current);
|
||||
}
|
||||
|
||||
/* Whether the shown tab's focused surface asked for async presentation. */
|
||||
static bool shown_surface_wants_tearing(void) {
|
||||
if (!server.tearing_mgr)
|
||||
return false;
|
||||
struct kick_window *w = tab_current_window(server.active_tab);
|
||||
struct wlr_surface *s = window_surface(w);
|
||||
if (!s)
|
||||
return false;
|
||||
return wlr_tearing_control_manager_v1_surface_hint_from_surface(
|
||||
server.tearing_mgr, s) ==
|
||||
WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
|
||||
}
|
||||
|
||||
/* Apply the shown surface's hint to an output commit. Logs transitions
|
||||
only, so steady vsync (the common case) stays quiet at debug level. */
|
||||
static void apply_tearing_hint(struct wlr_output_state *state) {
|
||||
bool async = shown_surface_wants_tearing();
|
||||
state->tearing_page_flip = async;
|
||||
static bool last_async = false;
|
||||
if (async != last_async) {
|
||||
last_async = async;
|
||||
struct kick_window *w = tab_current_window(server.active_tab);
|
||||
kick_log(KICK_LOG_DEBUG, "[tearing] %s app=%d\n", async ? "async" : "vsync",
|
||||
w ? tab_index(w->tab) : -1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== Keyboard shortcuts inhibit =====================
|
||||
A fullscreen game/VM asks that the compositor stop processing its keyboard
|
||||
shortcuts so they reach the client instead. Honored only while the
|
||||
|
|
@ -9898,6 +9967,7 @@ static void render_standalone(void) {
|
|||
dump_standalone_buffer(state.buffer);
|
||||
}
|
||||
|
||||
apply_tearing_hint(&state);
|
||||
if (!wlr_output_commit_state(server.output, &state))
|
||||
kick_log(KICK_LOG_DEBUG, "[render] output commit failed\n");
|
||||
|
||||
|
|
@ -9981,6 +10051,7 @@ static void render_and_present(void) {
|
|||
|
||||
blit_scene_buffer(state.buffer);
|
||||
|
||||
apply_tearing_hint(&state);
|
||||
if (!wlr_output_commit_state(server.output, &state))
|
||||
kick_log(KICK_LOG_DEBUG, "[render] output commit failed\n");
|
||||
|
||||
|
|
@ -10998,9 +11069,9 @@ 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. */
|
||||
/* Tearing-control + fractional-scale: the shown surface's tearing hint
|
||||
drives the commit's async-flip flag (see apply_tearing_hint), and
|
||||
surfaces learn the output scale at claim. */
|
||||
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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue