test tear

This commit is contained in:
DreamMaoMao 2025-10-30 18:50:18 +08:00
parent 05e9d149c1
commit e72b9f4429
6 changed files with 186 additions and 12 deletions

View file

@ -19,6 +19,7 @@ wayland_xmls = [
wl_protocol_dir + '/staging/ext-image-capture-source/ext-image-capture-source-v1.xml',
wl_protocol_dir + '/staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml',
wl_protocol_dir + '/staging/ext-workspace/ext-workspace-v1.xml',
wl_protocol_dir + '/staging/tearing-control/tearing-control-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
'dwl-ipc-unstable-v2.xml',
'wlr-layer-shell-unstable-v1.xml',

View file

@ -153,6 +153,20 @@ static inline void client_get_geometry(Client *c, struct wlr_box *geom) {
*geom = c->surface.xdg->geometry;
}
static inline Client *get_client_from_surface(struct wlr_surface *surface) {
if (!surface)
return NULL;
// 从 surface 的 data 指针获取 scene tree
struct wlr_scene_tree *scene_tree = surface->data;
if (!scene_tree)
return NULL;
// 从 scene tree 的 node data 获取 Client
Client *c = scene_tree->node.data;
return c;
}
static inline Client *client_get_parent(Client *c) {
Client *p = NULL;
#ifdef XWAYLAND

View file

@ -101,6 +101,7 @@ int xwayland_persistence = 1; /* xwayland persistence */
int syncobj_enable = 0;
int adaptive_sync = 0;
double drag_refresh_interval = 30.0;
int allow_tearing = TEARING_DISABLED;
/* keyboard */

View file

@ -1,4 +1,5 @@
#include "dwl-ipc.h"
#include "ext-workspace.h"
#include "foreign-toplevel.h"
#include "tearing.h"
#include "text-input.h"

106
src/ext-protocol/tearing.h Normal file
View file

@ -0,0 +1,106 @@
#include <wlr/types/wlr_tearing_control_v1.h>
struct tearing_controller {
struct wlr_tearing_control_v1 *tearing_control;
struct wl_listener set_hint;
struct wl_listener destroy;
};
typedef struct SendFrameDoneData {
struct timespec when;
struct Monitor *mon;
} SendFrameDoneData;
struct wlr_tearing_control_manager_v1 *tearing_control;
struct wl_listener tearing_new_object;
static void handle_controller_set_hint(struct wl_listener *listener,
void *data) {
struct tearing_controller *controller =
wl_container_of(listener, controller, set_hint);
Client *c = get_client_from_surface(controller->tearing_control->surface);
if (c) {
/*
* 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.
*/
c->tearing_hint = controller->tearing_control->current;
}
}
static void handle_controller_destroy(struct wl_listener *listener,
void *data) {
struct tearing_controller *controller =
wl_container_of(listener, controller, destroy);
wl_list_remove(&controller->set_hint.link);
wl_list_remove(&controller->destroy.link);
free(controller);
}
void handle_tearing_new_object(struct wl_listener *listener, void *data) {
struct wlr_tearing_control_v1 *new_tearing_control = data;
enum wp_tearing_control_v1_presentation_hint hint =
wlr_tearing_control_manager_v1_surface_hint_from_surface(
tearing_control, new_tearing_control->surface);
wlr_log(WLR_DEBUG, "New presentation hint %d received for surface %p", hint,
new_tearing_control->surface);
struct tearing_controller *controller =
ecalloc(1, sizeof(struct tearing_controller));
controller->tearing_control = new_tearing_control;
controller->set_hint.notify = handle_controller_set_hint;
wl_signal_add(&new_tearing_control->events.set_hint, &controller->set_hint);
controller->destroy.notify = handle_controller_destroy;
wl_signal_add(&new_tearing_control->events.destroy, &controller->destroy);
}
bool output_get_tearing_allowance(Monitor *m) {
/* never allow tearing when disabled */
if (!allow_tearing) {
return false;
}
Client *c = selmon->sel;
/* tearing is only allowed for the output with the active client */
if (!c || c->mon != m) {
return false;
}
/* allow tearing for any window when requested or forced */
if (allow_tearing == TEARING_ENABLED) {
if (c->force_tearing == STATE_UNSPECIFIED) {
return c->tearing_hint;
} else {
return c->force_tearing == STATE_ENABLED;
}
}
/* remaining tearing options apply only to full-screen windows */
if (!c->isfullscreen) {
return false;
}
if (c->force_tearing == STATE_UNSPECIFIED) {
/* honor the tearing hint or the fullscreen-force preference */
return c->tearing_hint || allow_tearing == TEARING_FULLSCREEN_FORCED;
}
/* honor tearing as requested by action */
return c->force_tearing == STATE_ENABLED;
}
void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y,
void *user_data) {
SendFrameDoneData *data = user_data;
if (buffer->primary_output != data->mon->scene_output)
return;
wlr_scene_buffer_send_frame_done(buffer, &data->when);
}

View file

@ -164,6 +164,14 @@ enum { UP, DOWN, LEFT, RIGHT, UNDIR }; /* smartmovewin */
enum { NONE, OPEN, MOVE, CLOSE, TAG };
enum { UNFOLD, FOLD, INVALIDFOLD };
enum { PREV, NEXT };
enum { STATE_UNSPECIFIED = 0, STATE_ENABLED, STATE_DISABLED };
enum tearing_mode {
TEARING_DISABLED = 0,
TEARING_ENABLED,
TEARING_FULLSCREEN,
TEARING_FULLSCREEN_FORCED,
};
typedef struct Pertag Pertag;
typedef struct Monitor Monitor;
@ -347,6 +355,8 @@ struct Client {
bool ismaster;
bool cursor_in_upper_half, cursor_in_left_half;
bool isleftstack;
bool tearing_hint;
bool force_tearing;
};
typedef struct {
@ -1940,6 +1950,7 @@ void cleanuplisteners(void) {
wl_list_remove(&start_drag.link);
wl_list_remove(&new_session_lock.link);
wl_list_remove(&drm_lease_request.link);
wl_list_remove(&tearing_new_object.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
wl_list_remove(&xwayland_ready.link);
@ -4078,10 +4089,27 @@ void rendermon(struct wl_listener *listener, void *data) {
struct timespec now;
bool need_more_frames = false;
bool skip_commit = false; // 新增:控制是否跳过提交
struct wlr_scene_output *scene_output = m->scene_output;
SendFrameDoneData frame_done_data = {0};
m->wlr_output->frame_pending = false;
if (!wlr_scene_output_needs_frame(scene_output)) {
goto skip;
}
// // 初始化输出状态
wlr_output_state_init(&pending);
if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) {
wlr_output_state_finish(&pending);
goto skip;
}
// 绘制层和淡出效果
for (i = 0; i < LENGTH(m->layers); i++) {
layer_list = &m->layers[i];
// Draw frames for all layer
wl_list_for_each_safe(l, tmpl, layer_list, link) {
need_more_frames = layer_draw_frame(l) || need_more_frames;
}
@ -4095,24 +4123,43 @@ void rendermon(struct wl_listener *listener, void *data) {
need_more_frames = layer_draw_fadeout_frame(l) || need_more_frames;
}
// Draw frames for all clients
// 绘制客户端并检查是否需要跳过提交
wl_list_for_each(c, &clients, link) {
need_more_frames = client_draw_frame(c) || need_more_frames;
if (!animations && c->configure_serial && !c->isfloating &&
client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
goto skip;
if (!animations && allow_tearing && c->configure_serial &&
!c->isfloating && client_is_rendered_on_mon(c, m) &&
!client_is_stopped(c)) {
skip_commit = true; // 标记跳过提交,但不中断循环
}
}
wlr_scene_output_commit(m->scene_output, NULL);
// 如果不需要跳过提交,才进行实际提交
if (!skip_commit) {
if (output_get_tearing_allowance(m)) {
pending.tearing_page_flip = true;
if (!wlr_output_test_state(m->wlr_output, &pending)) {
fprintf(stderr,
"Output test failed on '%s', retrying without tearing "
"page-flip\n",
m->wlr_output->name);
pending.tearing_page_flip = false;
}
}
if (!wlr_output_commit_state(m->wlr_output, &pending))
fprintf(stderr, "Page-flip failed on output %s",
m->wlr_output->name);
}
wlr_output_state_finish(&pending);
skip:
// Send frame done notification
// 发送帧完成通知
clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(m->scene_output, &now);
// // Clean up pending state
wlr_output_state_finish(&pending);
frame_done_data.mon = m;
wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator,
&frame_done_data);
if (need_more_frames) {
wlr_output_schedule_frame(m->wlr_output);
@ -4900,6 +4947,10 @@ void setup(void) {
power_mgr = wlr_output_power_manager_v1_create(dpy);
wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
tearing_control = wlr_tearing_control_manager_v1_create(dpy, 1);
tearing_new_object.notify = handle_tearing_new_object;
wl_signal_add(&tearing_control->events.new_object, &tearing_new_object);
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
output_layout = wlr_output_layout_create(dpy);