summaryrefslogtreecommitdiffstats
path: root/patches/tearing.patch
diff options
context:
space:
mode:
authorkorei999 <ju7t1xe@gmail.com>2024-09-18 20:11:22 +0300
committerSquibid <me@zacharyscheiman.com>2025-02-11 23:53:50 -0600
commit5f7804bd6b22bbcaba5e0bd1e21381031a659813 (patch)
tree391aa9eb4f7b623230885382868c115cd196c30c /patches/tearing.patch
parent95065adb2a1081d9d1a2ad6a3cdda6f5f80bbb4a (diff)
downloaddwl-5f7804bd6b22bbcaba5e0bd1e21381031a659813.tar.gz
dwl-5f7804bd6b22bbcaba5e0bd1e21381031a659813.tar.bz2
dwl-5f7804bd6b22bbcaba5e0bd1e21381031a659813.zip
implement tearing protocol
Diffstat (limited to 'patches/tearing.patch')
-rw-r--r--patches/tearing.patch356
1 files changed, 356 insertions, 0 deletions
diff --git a/patches/tearing.patch b/patches/tearing.patch
new file mode 100644
index 0000000..951d25c
--- /dev/null
+++ b/patches/tearing.patch
@@ -0,0 +1,356 @@
+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
+