Skip to content

Instantly share code, notes, and snippets.

@rhoot

rhoot/sway.patch Secret

Created September 2, 2023 04:05
Show Gist options
  • Save rhoot/5d47b0aabe96f6d44659d2f40329860f to your computer and use it in GitHub Desktop.
Save rhoot/5d47b0aabe96f6d44659d2f40329860f to your computer and use it in GitHub Desktop.
YellowOnion's vrr cursor patches updated for latest master
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 27058587..d7593d58 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -152,6 +152,7 @@ sway_cmd cmd_kill;
sway_cmd cmd_layout;
sway_cmd cmd_log_colors;
sway_cmd cmd_mark;
+sway_cmd cmd_max_cursor_latency;
sway_cmd cmd_max_render_time;
sway_cmd cmd_mode;
sway_cmd cmd_mouse_warping;
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 960f9d71..ab00e83b 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -122,6 +122,7 @@ struct sway_view {
struct wl_listener surface_new_subsurface;
int max_render_time; // In milliseconds
+ int max_cursor_latency; // In microseconds
enum seat_config_shortcuts_inhibit shortcuts_inhibit;
};
diff --git a/sway/commands.c b/sway/commands.c
index 55eda183..d8b28f42 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -122,6 +122,7 @@ static const struct cmd_handler command_handlers[] = {
{ "kill", cmd_kill },
{ "layout", cmd_layout },
{ "mark", cmd_mark },
+ { "max_cursor_latency", cmd_max_cursor_latency },
{ "max_render_time", cmd_max_render_time },
{ "move", cmd_move },
{ "nop", cmd_nop },
diff --git a/sway/commands/max_cursor_latency.c b/sway/commands/max_cursor_latency.c
new file mode 100644
index 00000000..12654c1d
--- /dev/null
+++ b/sway/commands/max_cursor_latency.c
@@ -0,0 +1,32 @@
+#include <strings.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/tree/view.h"
+
+struct cmd_results *cmd_max_cursor_latency(int argc, char **argv) {
+ if (!argc) {
+ return cmd_results_new(CMD_INVALID, "Missing max cursor latency argument.");
+ }
+
+ int max_cursor_latency;
+ if (!strcmp(*argv, "off")) {
+ max_cursor_latency = 0;
+ } else {
+ char *end;
+ max_cursor_latency = strtol(*argv, &end, 10);
+ if (*end || max_cursor_latency <= 0) {
+ return cmd_results_new(CMD_INVALID, "Invalid max cursor latency.");
+ }
+ }
+
+ struct sway_container *container = config->handler_context.container;
+ if (!container || !container->view) {
+ return cmd_results_new(CMD_INVALID,
+ "Only views can have a max_cursor_latency");
+ }
+
+ struct sway_view *view = container->view;
+ view->max_cursor_latency = max_cursor_latency;
+
+ return cmd_results_new(CMD_SUCCESS, NULL);
+}
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index aed1fdeb..d1376601 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -565,6 +565,11 @@ static int output_repaint_timer_handler(void *data) {
wlr_output->frame_pending = false;
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ wlr_output_cursor_move_any_expired(wlr_output, &now);
+
if (!wlr_output->needs_frame &&
!output->gamma_lut_changed &&
!pixman_region32_not_empty(&output->damage_ring.current)) {
@@ -601,6 +606,9 @@ static int output_repaint_timer_handler(void *data) {
pending.committed |= WLR_OUTPUT_STATE_DAMAGE;
get_frame_damage(output, &pending.damage);
+ if (output->wlr_output->needs_frame)
+ wlr_output_cursor_move_all_deferred(wlr_output, &now);
+
if (fullscreen_con && fullscreen_con->view && !debug.noscanout) {
// Try to scan-out the fullscreen view
static bool last_scanned_out = false;
@@ -657,9 +665,6 @@ static int output_repaint_timer_handler(void *data) {
.pass = render_pass,
};
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
-
output_render(&ctx);
pixman_region32_fini(&damage);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index a84e4d52..e9c2af47 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -1222,9 +1222,11 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n
seat_send_focus(&container->node, seat);
}
+ int max_cursor_latency = 0;
// emit ipc events
set_workspace(seat, new_workspace);
if (container && container->view) {
+ max_cursor_latency = container->view->max_cursor_latency;
ipc_event_window(container, "focus");
}
@@ -1274,6 +1276,9 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n
workspace_consider_destroy(last_workspace);
}
+ if (seat->cursor)
+ wlr_cursor_set_max_latency(seat->cursor->cursor, max_cursor_latency * 1000);
+
seat->has_focus = true;
if (config->smart_gaps && new_workspace) {
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index 58356d4e..48bf63fe 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -588,6 +588,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry));
json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time));
+ json_object_object_add(object, "max_cursor_latency", json_object_new_int(c->view->max_cursor_latency));
json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view)));
diff --git a/sway/meson.build b/sway/meson.build
index 3abd778d..550c2290 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -73,6 +73,7 @@ sway_sources = files(
'commands/kill.c',
'commands/mark.c',
'commands/max_render_time.c',
+ 'commands/max_cursor_latency.c',
'commands/opacity.c',
'commands/include.c',
'commands/input.c',
diff --git a/sway/tree/view.c b/sway/tree/view.c
index ec54fed8..7f9c3b67 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -4,6 +4,7 @@
#include <wayland-server-core.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
+#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_subcompositor.h>
diff --git a/include/util/time.h b/include/util/time.h
index 3f76aa47..1fcb569c 100644
--- a/include/util/time.h
+++ b/include/util/time.h
@@ -4,6 +4,8 @@
#include <stdint.h>
#include <time.h>
+static const long NSEC_PER_SEC = 1000000000;
+
/**
* Get the current time, in milliseconds.
*/
@@ -30,4 +32,6 @@ void timespec_from_nsec(struct timespec *r, int64_t nsec);
void timespec_sub(struct timespec *r, const struct timespec *a,
const struct timespec *b);
+int32_t mhz_to_nsec(int32_t mhz);
+
#endif
diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h
index 004be0f1..c29b9e22 100644
--- a/include/wlr/types/wlr_cursor.h
+++ b/include/wlr/types/wlr_cursor.h
@@ -34,6 +34,8 @@ struct wlr_cursor {
struct wlr_cursor_state *state;
double x, y;
+ int max_latency;
+
/**
* The interpretation of these signals is the responsibility of the
* compositor, but some helpers are provided for your benefit. If you
@@ -166,6 +168,8 @@ void wlr_cursor_set_xcursor(struct wlr_cursor *cur,
void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface,
int32_t hotspot_x, int32_t hotspot_y);
+void wlr_cursor_set_max_latency(struct wlr_cursor *cur, int max_latency);
+
/**
* Attaches this input device to this cursor. The input device must be one of:
*
diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h
index 30331fba..7343fb51 100644
--- a/include/wlr/types/wlr_output.h
+++ b/include/wlr/types/wlr_output.h
@@ -46,6 +46,10 @@ struct wlr_output_cursor {
struct wlr_texture *texture;
bool own_texture;
struct wl_list link;
+ struct timespec last_presentation;
+ bool deferred;
+ double deferred_x, deferred_y;
+ int max_latency;
};
enum wlr_output_adaptive_sync_status {
@@ -144,6 +148,8 @@ struct wlr_output {
enum wlr_output_adaptive_sync_status adaptive_sync_status;
uint32_t render_format;
+ int max_cursor_latency;
+
bool needs_frame;
// damage for cursors and fullscreen surface, in output-local coordinates
bool frame_pending;
@@ -556,6 +562,13 @@ bool wlr_output_cursor_set_buffer(struct wlr_output_cursor *cursor,
struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y);
bool wlr_output_cursor_move(struct wlr_output_cursor *cursor,
double x, double y);
+/**
+ * Call any_expired() before you check needs_frame.
+ * call all_deferred() whenever a new frame is needed.
+ * */
+void wlr_output_cursor_move_expired(struct wlr_output_cursor *cursor, struct timespec *now);
+void wlr_output_cursor_move_any_expired(struct wlr_output *output, struct timespec *now);
+void wlr_output_cursor_move_all_deferred(struct wlr_output *output, struct timespec *now);
void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor);
/**
diff --git a/types/output/cursor.c b/types/output/cursor.c
index 4d0cc19a..fd61cec3 100644
--- a/types/output/cursor.c
+++ b/types/output/cursor.c
@@ -1,6 +1,8 @@
+#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <drm_fourcc.h>
#include <stdlib.h>
+#include <time.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
@@ -8,6 +10,7 @@
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
+#include <util/time.h>
#include "render/allocator/allocator.h"
#include "types/wlr_buffer.h"
#include "types/wlr_output.h"
@@ -461,15 +464,10 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor,
return true;
}
-bool wlr_output_cursor_move(struct wlr_output_cursor *cursor,
- double x, double y) {
- // Scale coordinates for the output
- x *= cursor->output->scale;
- y *= cursor->output->scale;
-
- if (cursor->x == x && cursor->y == y) {
- return true;
- }
+static bool output_cursor_move(struct wlr_output_cursor *cursor,
+ double x, double y, struct timespec *now) {
+ cursor->last_presentation = *now;
+ cursor->deferred = false;
if (cursor->output->hardware_cursor != cursor) {
output_cursor_damage_whole(cursor);
@@ -494,6 +492,80 @@ bool wlr_output_cursor_move(struct wlr_output_cursor *cursor,
return cursor->output->impl->move_cursor(cursor->output, (int)x, (int)y);
}
+static bool output_cursor_move_should_defer(struct wlr_output_cursor *cursor,
+ struct timespec *now) {
+ int max_latency;
+
+ max_latency = cursor->max_latency;
+
+ if (!max_latency)
+ max_latency = cursor->output->max_cursor_latency;
+
+ if (!max_latency
+ || !cursor->output->refresh // avoid divide by zero
+ || cursor->output->adaptive_sync_status != WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)
+ return false;
+
+ struct timespec delta;
+ int32_t vrr_min = NSEC_PER_SEC / 30; // edid? enforce 30fps minimum for now.
+ int32_t vrr_max = mhz_to_nsec(cursor->output->refresh);
+ // We want to move cursor *before* it's expired.
+ timespec_sub(&delta, now, &cursor->last_presentation);
+ if (delta.tv_sec
+ || delta.tv_nsec + vrr_max >= max_latency
+ || delta.tv_nsec >= vrr_min)
+ return false;
+
+ return true;
+}
+
+bool wlr_output_cursor_move(struct wlr_output_cursor *cursor,
+ double x, double y) {
+ // Scale coordinates for the output
+ x *= cursor->output->scale;
+ y *= cursor->output->scale;
+
+ if (cursor->x == x && cursor->y == y) {
+ return true;
+ }
+
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if (output_cursor_move_should_defer(cursor, &now)) {
+ cursor->deferred_x = x;
+ cursor->deferred_y = y;
+ cursor->deferred = true;
+ return true;
+ }
+
+ return output_cursor_move(cursor, x, y, &now);
+}
+
+void wlr_output_cursor_move_expired(struct wlr_output_cursor *cursor, struct timespec *now) {
+ if (cursor->deferred && !output_cursor_move_should_defer(cursor, now))
+ output_cursor_move(cursor, cursor->deferred_x, cursor->deferred_y, now);
+}
+
+void wlr_output_cursor_move_any_expired(struct wlr_output *output, struct timespec *now) {
+ struct wlr_output_cursor *cursor;
+ wl_list_for_each(cursor, &output->cursors, link) {
+ wlr_output_cursor_move_expired(cursor, now);
+ }
+}
+
+void wlr_output_cursor_move_all_deferred(struct wlr_output *output, struct timespec *now) {
+ struct wlr_output_cursor *cursor;
+ wl_list_for_each(cursor, &output->cursors, link) {
+ if (cursor->deferred)
+ output_cursor_move(cursor, cursor->deferred_x, cursor->deferred_y, now);
+ else {
+ // Should be on wlr_output?
+ cursor->last_presentation = *now;
+ };
+ }
+}
+
struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output) {
struct wlr_output_cursor *cursor =
calloc(1, sizeof(struct wlr_output_cursor));
diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c
index a228bc9f..491b6eaa 100644
--- a/types/wlr_cursor.c
+++ b/types/wlr_cursor.c
@@ -277,6 +277,7 @@ static void cursor_warp_unchecked(struct wlr_cursor *cur,
double output_x = lx, output_y = ly;
wlr_output_layout_output_coords(cur->state->layout,
output_cursor->output_cursor->output, &output_x, &output_y);
+ output_cursor->output_cursor->max_latency = cur->max_latency;
wlr_output_cursor_move(output_cursor->output_cursor,
output_x, output_y);
}
@@ -648,6 +649,11 @@ void wlr_cursor_set_surface(struct wlr_cursor *cur, struct wlr_surface *surface,
cursor_update_outputs(cur);
}
+void wlr_cursor_set_max_latency(struct wlr_cursor *cur, int max_latency) {
+ cur->max_latency = max_latency;
+ wlr_log(WLR_DEBUG, "setting max_latency %i", max_latency);
+}
+
static void handle_pointer_motion(struct wl_listener *listener, void *data) {
struct wlr_pointer_motion_event *event = data;
struct wlr_cursor_device *device =
diff --git a/util/time.c b/util/time.c
index 78faac56..6a5fa010 100644
--- a/util/time.c
+++ b/util/time.c
@@ -4,8 +4,6 @@
#include "util/time.h"
-static const long NSEC_PER_SEC = 1000000000;
-
int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
@@ -34,3 +32,7 @@ void timespec_sub(struct timespec *r, const struct timespec *a,
r->tv_nsec += NSEC_PER_SEC;
}
}
+
+int32_t mhz_to_nsec(int32_t mhz) {
+ return 1000000000000LL / mhz;
+}
@rhoot
Copy link
Author

rhoot commented Dec 14, 2023

Unfortunately I'm away from home for probably the next month. I won't be able to update them until I get back. Hopefully this can just get merged at some point in the not too distant future.

@GrabbenD
Copy link

Just updated to swaywm/sway#7780 and I'm not seeing any issues any longer 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment