mirror of
				https://codeberg.org/dwl/dwl-patches.git
				synced 2025-10-31 03:54:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 66b2e1646bee8502a3715403c165015bd019438c Mon Sep 17 00:00:00 2001
 | |
| From: korei999 <ju7t1xe@gmail.com>
 | |
| Date: Wed, 18 Sep 2024 20:11:22 +0300
 | |
| Subject: [PATCH] implement tearing protocol
 | |
| 
 | |
| ---
 | |
|  Makefile     |   5 +-
 | |
|  config.def.h |   8 +++
 | |
|  dwl.c        | 184 +++++++++++++++++++++++++++++++++++++++++++++++----
 | |
|  3 files changed, 182 insertions(+), 15 deletions(-)
 | |
| 
 | |
| diff --git a/Makefile b/Makefile
 | |
| index 8db7409..6edc7d7 100644
 | |
| --- a/Makefile
 | |
| +++ b/Makefile
 | |
| @@ -21,7 +21,7 @@ dwl: dwl.o util.o
 | |
|  	$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
 | |
|  dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
 | |
|  	pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
 | |
| -	wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
 | |
| +	wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h tearing-control-v1-protocol.h
 | |
|  util.o: util.c util.h
 | |
|  
 | |
|  # wayland-scanner is a tool which generates C headers and rigging for Wayland
 | |
| @@ -45,6 +45,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
 | |
|  xdg-shell-protocol.h:
 | |
|  	$(WAYLAND_SCANNER) server-header \
 | |
|  		$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
 | |
| +tearing-control-v1-protocol.h:
 | |
| +	$(WAYLAND_SCANNER) server-header \
 | |
| +		$(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@
 | |
|  
 | |
|  config.h:
 | |
|  	cp config.def.h $@
 | |
| diff --git a/config.def.h b/config.def.h
 | |
| index 22d2171..52d38d3 100644
 | |
| --- a/config.def.h
 | |
| +++ b/config.def.h
 | |
| @@ -28,6 +28,14 @@ static const Rule rules[] = {
 | |
|  	{ "firefox_EXAMPLE",  NULL,       1 << 8,       0,           -1 }, /* Start on ONLY tag "9" */
 | |
|  };
 | |
|  
 | |
| +/* tearing */
 | |
| +static int tearing_allowed = 1;
 | |
| +static const ForceTearingRule force_tearing[] = {
 | |
| +	{.title = "", .appid = "hl_linux"},
 | |
| +	{.title = "Warcraft III", .appid = ""},
 | |
| +	{.title = "", .appid = "gamescope"},
 | |
| +};
 | |
| +
 | |
|  /* layout(s) */
 | |
|  static const Layout layouts[] = {
 | |
|  	/* symbol     arrange function */
 | |
| diff --git a/dwl.c b/dwl.c
 | |
| index dc0c861..44be1bf 100644
 | |
| --- a/dwl.c
 | |
| +++ b/dwl.c
 | |
| @@ -51,6 +51,7 @@
 | |
|  #include <wlr/types/wlr_session_lock_v1.h>
 | |
|  #include <wlr/types/wlr_single_pixel_buffer_v1.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>
 | |
| @@ -90,6 +91,11 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
 | |
|  	NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
 | |
|  #endif
 | |
|  
 | |
| +typedef struct ForceTearingRule {
 | |
| +	const char* title;
 | |
| +	const char* appid;
 | |
| +} ForceTearingRule;
 | |
| +
 | |
|  typedef union {
 | |
|  	int i;
 | |
|  	uint32_t ui;
 | |
| @@ -143,6 +149,7 @@ typedef struct {
 | |
|  	uint32_t tags;
 | |
|  	int isfloating, isurgent, isfullscreen;
 | |
|  	uint32_t resize; /* configure serial of a pending resize */
 | |
| +	enum wp_tearing_control_v1_presentation_hint tearing_hint;
 | |
|  } Client;
 | |
|  
 | |
|  typedef struct {
 | |
| @@ -243,6 +250,19 @@ typedef struct {
 | |
|  	struct wl_listener destroy;
 | |
|  } SessionLock;
 | |
|  
 | |
| +typedef struct TearingController {
 | |
| +	struct wlr_tearing_control_v1 *tearing_control;
 | |
| +	struct wl_listener set_hint;
 | |
| +	struct wl_listener destroy;
 | |
| +
 | |
| +	struct wl_list link; /* tearing_controllers */
 | |
| +} TearingController;
 | |
| +
 | |
| +typedef struct SendFrameDoneData {
 | |
| +	struct timespec when;
 | |
| +	struct Monitor *mon;
 | |
| +} SendFrameDoneData;
 | |
| +
 | |
|  /* function declarations */
 | |
|  static void applybounds(Client *c, struct wlr_box *bbox);
 | |
|  static void applyrules(Client *c);
 | |
| @@ -293,6 +313,9 @@ static Client *focustop(Monitor *m);
 | |
|  static void fullscreennotify(struct wl_listener *listener, void *data);
 | |
|  static void gpureset(struct wl_listener *listener, void *data);
 | |
|  static void handlesig(int signo);
 | |
| +static void handletearingcontrollersethint(struct wl_listener *listener, void *data);
 | |
| +static void handletearingcontrollerdestroy(struct wl_listener *listener, void *data);
 | |
| +static void handlenewtearinghint(struct wl_listener *listener, void *data);
 | |
|  static void incnmaster(const Arg *arg);
 | |
|  static void inputdevice(struct wl_listener *listener, void *data);
 | |
|  static int keybinding(uint32_t mods, xkb_keysym_t sym);
 | |
| @@ -309,6 +332,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double
 | |
|  		double sy, double sx_unaccel, double sy_unaccel);
 | |
|  static void motionrelative(struct wl_listener *listener, void *data);
 | |
|  static void moveresize(const Arg *arg);
 | |
| +static int moncantear(Monitor* m);
 | |
|  static void outputmgrapply(struct wl_listener *listener, void *data);
 | |
|  static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
 | |
|  static void outputmgrtest(struct wl_listener *listener, void *data);
 | |
| @@ -323,6 +347,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
 | |
|  static void requestmonstate(struct wl_listener *listener, void *data);
 | |
|  static void resize(Client *c, struct wlr_box geo, int interact);
 | |
|  static void run(char *startup_cmd);
 | |
| +static void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data);
 | |
|  static void setcursor(struct wl_listener *listener, void *data);
 | |
|  static void setcursorshape(struct wl_listener *listener, void *data);
 | |
|  static void setfloating(Client *c, int floating);
 | |
| @@ -400,6 +425,10 @@ static struct wlr_scene_rect *locked_bg;
 | |
|  static struct wlr_session_lock_v1 *cur_lock;
 | |
|  static struct wl_listener lock_listener = {.notify = locksession};
 | |
|  
 | |
| +struct wlr_tearing_control_manager_v1 *tearing_control_v1;
 | |
| +struct wl_listener tearing_control_new_object;
 | |
| +struct wl_list tearing_controllers;
 | |
| +
 | |
|  static struct wlr_seat *seat;
 | |
|  static KeyboardGroup *kb_group;
 | |
|  static unsigned int cursor_mode;
 | |
| @@ -1510,6 +1539,69 @@ handlesig(int signo)
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| +void
 | |
| +handletearingcontrollersethint(struct wl_listener *listener, void *data)
 | |
| +{
 | |
| +	Client *c = NULL, *i = NULL;
 | |
| +	struct TearingController *controller = wl_container_of(listener, controller, set_hint);
 | |
| +
 | |
| +	struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface);
 | |
| +#ifdef XWAYLAND
 | |
| +	struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface);
 | |
| +#endif
 | |
| +
 | |
| +	wl_list_for_each(i, &fstack, flink) {
 | |
| +		if (i->surface.xdg == surface
 | |
| +#ifdef XWAYLAND
 | |
| +				|| i->surface.xwayland == xsurface
 | |
| +#endif
 | |
| +		   ) {
 | |
| +			c = i;
 | |
| +			break;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	if (c) {
 | |
| +		enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current;
 | |
| +		fprintf(
 | |
| +			stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n",
 | |
| +			(void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC"
 | |
| +		);
 | |
| +		c->tearing_hint = controller->tearing_control->current;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +void
 | |
| +handletearingcontrollerdestroy(struct wl_listener *listener, void *data)
 | |
| +{
 | |
| +	struct TearingController *controller = wl_container_of(listener, controller, destroy);
 | |
| +
 | |
| +	wl_list_remove(&controller->set_hint.link);
 | |
| +	wl_list_remove(&controller->destroy.link);
 | |
| +	wl_list_remove(&controller->link);
 | |
| +	free(controller);
 | |
| +}
 | |
| +
 | |
| +void
 | |
| +handlenewtearinghint(struct wl_listener *listener, void *data)
 | |
| +{
 | |
| +	struct wlr_tearing_control_v1 *tearing_control = data;
 | |
| +	struct TearingController *controller = calloc(1, sizeof(struct TearingController));
 | |
| +
 | |
| +	if (!controller)
 | |
| +		return;
 | |
| +
 | |
| +	controller->tearing_control = tearing_control;
 | |
| +	controller->set_hint.notify = handletearingcontrollersethint;
 | |
| +	wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
 | |
| +
 | |
| +	controller->destroy.notify = handletearingcontrollerdestroy;
 | |
| +	wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
 | |
| +
 | |
| +	wl_list_init(&controller->link);
 | |
| +	wl_list_insert(&tearing_controllers, &controller->link);
 | |
| +}
 | |
| +
 | |
|  void
 | |
|  incnmaster(const Arg *arg)
 | |
|  {
 | |
| @@ -1677,6 +1769,33 @@ locksession(struct wl_listener *listener, void *data)
 | |
|  	wlr_session_lock_v1_send_locked(session_lock);
 | |
|  }
 | |
|  
 | |
| +static inline void
 | |
| +forcetearingrule(Client *c)
 | |
| +{
 | |
| +	int success = 0;
 | |
| +	const char* appid = client_get_appid(c);
 | |
| +	const char* title = client_get_title(c);
 | |
| +
 | |
| +	for (unsigned i = 0; i < LENGTH(force_tearing); i++) {
 | |
| +		if (appid)
 | |
| +			if (strcmp(force_tearing[i].appid, appid) == 0) {
 | |
| +				success = 1;
 | |
| +				break;
 | |
| +			}
 | |
| +
 | |
| +		if (title)
 | |
| +			if (strcmp(force_tearing[i].title, title) == 0) {
 | |
| +				success = 1;
 | |
| +				break;
 | |
| +			}
 | |
| +	}
 | |
| +
 | |
| +	if (success) {
 | |
| +		c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
 | |
| +		fprintf(stderr, "tearing forced for: appid: '%s', title: '%s'\n", appid, title);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
|  void
 | |
|  mapnotify(struct wl_listener *listener, void *data)
 | |
|  {
 | |
| @@ -1686,6 +1805,8 @@ mapnotify(struct wl_listener *listener, void *data)
 | |
|  	Monitor *m;
 | |
|  	int i;
 | |
|  
 | |
| +	forcetearingrule(c);
 | |
| +
 | |
|  	/* Create scene tree for this client and its border */
 | |
|  	c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
 | |
|  	/* Enabled later by a call to arrange() */
 | |
| @@ -1924,6 +2045,13 @@ moveresize(const Arg *arg)
 | |
|  	}
 | |
|  }
 | |
|  
 | |
| +int
 | |
| +moncantear(Monitor* m)
 | |
| +{
 | |
| +	Client *c = focustop(m);
 | |
| +	return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */
 | |
| +}
 | |
| +
 | |
|  void
 | |
|  outputmgrapply(struct wl_listener *listener, void *data)
 | |
|  {
 | |
| @@ -2093,27 +2221,40 @@ quit(const Arg *arg)
 | |
|  void
 | |
|  rendermon(struct wl_listener *listener, void *data)
 | |
|  {
 | |
| -	/* This function is called every time an output is ready to display a frame,
 | |
| -	 * generally at the output's refresh rate (e.g. 60Hz). */
 | |
|  	Monitor *m = wl_container_of(listener, m, frame);
 | |
| -	Client *c;
 | |
| +	struct wlr_scene_output *scene_output = m->scene_output;
 | |
|  	struct wlr_output_state pending = {0};
 | |
| -	struct timespec now;
 | |
| +	SendFrameDoneData frame_done_data = {0};
 | |
|  
 | |
| -	/* Render if no XDG clients have an outstanding resize and are visible on
 | |
| -	 * this monitor. */
 | |
| -	wl_list_for_each(c, &clients, link) {
 | |
| -		if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
 | |
| -			goto skip;
 | |
| +	m->wlr_output->frame_pending = false;
 | |
| +
 | |
| +	if (!wlr_scene_output_needs_frame(scene_output)) {
 | |
| +		goto skip;
 | |
|  	}
 | |
|  
 | |
| -	wlr_scene_output_commit(m->scene_output, NULL);
 | |
| +	wlr_output_state_init(&pending);
 | |
| +	if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL)) {
 | |
| +		goto skip;
 | |
| +	}
 | |
| +
 | |
| +	if (tearing_allowed && moncantear(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);
 | |
|  
 | |
| -skip:
 | |
| -	/* Let clients know a frame has been rendered */
 | |
| -	clock_gettime(CLOCK_MONOTONIC, &now);
 | |
| -	wlr_scene_output_send_frame_done(m->scene_output, &now);
 | |
|  	wlr_output_state_finish(&pending);
 | |
| +
 | |
| +skip:
 | |
| +	clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when);
 | |
| +	frame_done_data.mon = m;
 | |
| +	wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data);
 | |
|  }
 | |
|  
 | |
|  void
 | |
| @@ -2237,6 +2378,16 @@ run(char *startup_cmd)
 | |
|  	wl_display_run(dpy);
 | |
|  }
 | |
|  
 | |
| +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);
 | |
| +}
 | |
| +
 | |
|  void
 | |
|  setcursor(struct wl_listener *listener, void *data)
 | |
|  {
 | |
| @@ -2584,6 +2735,11 @@ setup(void)
 | |
|  	LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
 | |
|  	LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
 | |
|  
 | |
| +	tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1);
 | |
| +	tearing_control_new_object.notify = handlenewtearinghint;
 | |
| +	wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object);
 | |
| +	wl_list_init(&tearing_controllers);
 | |
| +
 | |
|  	/* Make sure XWayland clients don't connect to the parent X server,
 | |
|  	 * e.g when running in the x11 backend or the wayland backend and the
 | |
|  	 * compositor has Xwayland support */
 | |
| -- 
 | |
| 2.46.0
 | |
| 
 | 
